package starlog import ( "errors" "path/filepath" "strings" "testing" "time" ) func waitForRotate(t *testing.T, timeout time.Duration, cond func() bool, reason string) { t.Helper() deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { if cond() { return } time.Sleep(10 * time.Millisecond) } t.Fatalf("timeout waiting for condition: %s", reason) } func TestNewRotatePolicySinkNilPolicy(t *testing.T) { sink, err := NewRotatePolicySink(filepath.Join(testBinDir(t), "a.log"), true, nil, time.Second) if err == nil { t.Fatalf("expected error for nil rotate policy") } if sink != nil { t.Fatalf("sink should be nil when create fails") } } func TestManagedRotateBySizeSinkCreatesBackups(t *testing.T) { path := filepath.Join(testBinDir(t), "route.log") sink, err := NewManagedRotateBySizeSink(path, true, 64, 5*time.Millisecond, RotateManageOptions{ MaxBackups: 10, Pattern: "route.*.log", }) if err != nil { t.Fatalf("create sink failed: %v", err) } defer sink.Close() for idx := 0; idx < 40; idx++ { if err = sink.Write([]byte(strings.Repeat("x", 16))); err != nil { t.Fatalf("sink write failed: %v", err) } time.Sleep(3 * time.Millisecond) } waitForRotate(t, 2*time.Second, func() bool { matches, _ := filepath.Glob(filepath.Join(filepath.Dir(path), "route.*.log")) return len(matches) > 0 }, "rotating sink backups") } func TestRotatingFileSinkClose(t *testing.T) { path := filepath.Join(testBinDir(t), "close.log") sink, err := NewRotateBySizeSink(path, true, 1024, time.Second) if err != nil { t.Fatalf("create sink failed: %v", err) } if err = sink.Close(); err != nil { t.Fatalf("close failed: %v", err) } if err = sink.Write([]byte("after-close")); !errors.Is(err, ErrRotatingFileSinkClosed) { t.Fatalf("write after close should return ErrRotatingFileSinkClosed, got %v", err) } } func TestRotatingFileSinkPrefersArchivePathProvider(t *testing.T) { path := filepath.Join(testBinDir(t), "sink_provider.log") sink, err := NewRotatePolicySink(path, true, &rotatePreferArchivePathPolicy{}, 5*time.Millisecond) if err != nil { t.Fatalf("create sink failed: %v", err) } defer sink.Close() if err = sink.Write([]byte("first")); err != nil { t.Fatalf("first write failed: %v", err) } time.Sleep(10 * time.Millisecond) if err = sink.Write([]byte("second")); err != nil { t.Fatalf("second write failed: %v", err) } waitForRotate(t, 2*time.Second, func() bool { matches, _ := filepath.Glob(path + ".*.archive.bak") return len(matches) > 0 }, "rotating sink archive path provider") nextMatches, _ := filepath.Glob(path + ".*.next.bak") if len(nextMatches) > 0 { t.Fatalf("rotating sink should not use NextPath when ArchivePath is available") } }