Smail/test/backend/runtime_logs_test.go
smail fda4786489
Some checks are pending
Security Checks / security (push) Waiting to run
运行日志直接存入日志根目录并安全迁移旧文件
2026-09-09 00:14:43 +08:00

122 lines
3.2 KiB
Go

package runtimelog
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git.pcux.cn/xuans/Smail/utils/logpolicy"
)
func TestRuntimeLogCompressionAndExactRetention(t *testing.T) {
root := t.TempDir()
w, err := New(root)
if err != nil {
t.Fatal(err)
}
defer w.Close()
now := time.Date(2026, 9, 8, 12, 0, 0, 0, time.UTC)
cutoff := logpolicy.Cutoff(now)
w.now = func() time.Time { return cutoff }
for _, at := range []time.Time{cutoff.Add(-time.Hour), cutoff, cutoff.Add(time.Hour), now.Add(-8 * 24 * time.Hour), now} {
if err := w.WriteAt(at, "sample\nsecond line"); err != nil {
t.Fatal(err)
}
}
w.now = func() time.Time { return now }
ctx := context.Background()
if err := w.Maintain(ctx, now); err != nil {
t.Fatal(err)
}
files, err := w.Files()
if err != nil || len(files) != 3 {
t.Fatalf("files: %+v %v", files, err)
}
name := prefix + cutoff.Format("2006-01-02") + ".log.gz"
page, err := w.Read(ctx, name, 0, 100)
if err != nil || len(page.Lines) != 2 {
t.Fatalf("boundary: %+v %v", page, err)
}
if !strings.Contains(page.Lines[0], `\n`) {
t.Fatal("multiline event split")
}
// 压缩后仍须逐条裁剪,不能按整个归档日期删除有效记录。
now = now.Add(30 * time.Minute)
if err := w.Maintain(ctx, now); err != nil {
t.Fatal(err)
}
page, err = w.Read(ctx, name, 0, 100)
if err != nil || len(page.Lines) != 1 {
t.Fatalf("compressed boundary: %+v %v", page, err)
}
now = now.Add(24 * time.Hour)
if err := w.Maintain(ctx, now); err != nil {
t.Fatal(err)
}
if _, err := w.root.Stat(name); !os.IsNotExist(err) {
t.Fatalf("expired archive still exists: %v", err)
}
if _, err := w.Read(ctx, "../config/config.json", 0, 100); err == nil {
t.Fatal("path traversal accepted")
}
if err := w.Maintain(ctx, now); err != nil {
t.Fatal(err)
}
}
func TestRuntimeLogCorruptionAndUnknownFilesArePreserved(t *testing.T) {
root := t.TempDir()
w, err := New(root)
if err != nil {
t.Fatal(err)
}
defer w.Close()
now := time.Now().UTC()
w.now = func() time.Time { return now }
directory := filepath.Join(root, "log")
name := prefix + now.Add(-40*24*time.Hour).Format("2006-01-02") + ".log.gz"
if err := os.WriteFile(filepath.Join(directory, name), []byte("broken"), 0600); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(directory, "manual.log"), []byte("keep"), 0600); err != nil {
t.Fatal(err)
}
if err := w.Maintain(context.Background(), now); err == nil {
t.Fatal("corruption not reported")
}
for _, file := range []string{name, "manual.log"} {
if _, err := os.Stat(filepath.Join(directory, file)); err != nil {
t.Fatal("unmanaged or damaged evidence deleted")
}
}
}
func TestRuntimeLogCurrentWritesDoNotWaitForArchiveRead(t *testing.T) {
w, err := New(t.TempDir())
if err != nil {
t.Fatal(err)
}
defer w.Close()
now := time.Now()
if err := w.WriteAt(now, "first"); err != nil {
t.Fatal(err)
}
w.archiveMu.Lock()
done := make(chan error, 1)
go func() { done <- w.WriteAt(now, "second") }()
select {
case err := <-done:
w.archiveMu.Unlock()
if err != nil {
t.Fatal(err)
}
case <-time.After(time.Second):
w.archiveMu.Unlock()
<-done
t.Fatal("current log write waited for archive lock")
}
}