130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package applog
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func testService(t *testing.T) *Service {
|
|
t.Helper()
|
|
return &Service{dir: t.TempDir(), limit: maxFileBytes}
|
|
}
|
|
|
|
func TestRotationAndRestart(t *testing.T) {
|
|
s := testService(t)
|
|
// Fill exactly 2 MiB: no file may exceed the threshold and entries may not split.
|
|
line := strings.Repeat("x", 65535)
|
|
for i := 0; i < 32; i++ {
|
|
if err := s.Append(line); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
info, err := os.Stat(filepath.Join(s.dir, "runtime.log"))
|
|
if err != nil || info.Size() != maxFileBytes {
|
|
t.Fatalf("threshold: %v, %v", info, err)
|
|
}
|
|
if err := s.Append("[2026-11-04 12:34:56] 重启后仍可查看"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
files, err := s.files()
|
|
if err != nil || len(files) != 2 {
|
|
t.Fatalf("archives: %v, %v", files, err)
|
|
}
|
|
for _, path := range files {
|
|
info, err := os.Stat(path)
|
|
if err != nil || info.Size() > maxFileBytes {
|
|
t.Fatalf("oversized: %v, %v", info, err)
|
|
}
|
|
}
|
|
restarted := &Service{dir: s.dir, limit: maxFileBytes}
|
|
lines, err := restarted.Read()
|
|
if err != nil || len(lines) != 33 || !strings.Contains(lines[32], "重启") {
|
|
t.Fatalf("restore: %d, %v", len(lines), err)
|
|
}
|
|
if lines[0] != line {
|
|
t.Fatal("archive contents changed")
|
|
}
|
|
}
|
|
|
|
func TestRecentLimitAndClear(t *testing.T) {
|
|
s := testService(t)
|
|
s.limit = 20000
|
|
var content strings.Builder
|
|
for i := 0; i < 2200; i++ {
|
|
fmt.Fprintf(&content, "line %04d\n", i)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(s.dir, "runtime-20261103.log"), []byte(content.String()), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Append("latest\nmultiline\rerror"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lines, err := s.Read()
|
|
if err != nil || len(lines) != recentLines || lines[0] != "line 0201" || lines[len(lines)-1] != `latest\nmultiline\rerror` {
|
|
t.Fatalf("recent lines: %d %v", len(lines), err)
|
|
}
|
|
other := filepath.Join(s.dir, "unrelated.txt")
|
|
if err := os.WriteFile(other, []byte("keep"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Clear(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(other); err != nil {
|
|
t.Fatal("cleared unrelated file")
|
|
}
|
|
lines, err = s.Read()
|
|
if err != nil || len(lines) != 0 {
|
|
t.Fatalf("clear: %v %v", lines, err)
|
|
}
|
|
if err := s.Append("after clear"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lines, err = s.Read()
|
|
if err != nil || !reflect.DeepEqual(lines, []string{"after clear"}) {
|
|
t.Fatalf("append after clear: %v %v", lines, err)
|
|
}
|
|
}
|
|
|
|
func TestConcurrentAppendAndFailures(t *testing.T) {
|
|
s := testService(t)
|
|
s.limit = 30
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 20; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
if err := s.Append(fmt.Sprintf("event %02d", i)); err != nil {
|
|
t.Error(err)
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
lines, err := s.Read()
|
|
if err != nil || len(lines) != 20 {
|
|
t.Fatalf("concurrent: %v %v", lines, err)
|
|
}
|
|
seen := map[string]bool{}
|
|
for _, line := range lines {
|
|
seen[line] = true
|
|
}
|
|
if len(seen) != 20 {
|
|
t.Fatal("lost or duplicate entries")
|
|
}
|
|
if err := s.Append(strings.Repeat("x", maxEntryBytes+1)); err == nil {
|
|
t.Fatal("accepted oversized record")
|
|
}
|
|
broken := &Service{dir: filepath.Join(s.dir, "runtime.log"), limit: maxFileBytes}
|
|
if _, err := broken.Read(); err == nil {
|
|
t.Fatal("read failure hidden")
|
|
}
|
|
if err := broken.Append("no room"); err == nil {
|
|
t.Fatal("write failure hidden")
|
|
}
|
|
}
|