150 lines
4.3 KiB
Go
150 lines
4.3 KiB
Go
|
|
package starlog
|
||
|
|
|
||
|
|
import (
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strconv"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func writeFileWithModTime(t *testing.T, path string, modTime time.Time) {
|
||
|
|
t.Helper()
|
||
|
|
if err := os.WriteFile(path, []byte("x"), 0644); err != nil {
|
||
|
|
t.Fatalf("write file failed: %v", err)
|
||
|
|
}
|
||
|
|
if err := os.Chtimes(path, modTime, modTime); err != nil {
|
||
|
|
t.Fatalf("set mod time failed: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestApplyRotateManageOptionsCompress(t *testing.T) {
|
||
|
|
dir := testBinDir(t)
|
||
|
|
current := filepath.Join(dir, "app.log")
|
||
|
|
archive := filepath.Join(dir, "app.log.1")
|
||
|
|
|
||
|
|
writeFileWithModTime(t, current, time.Now())
|
||
|
|
writeFileWithModTime(t, archive, time.Now())
|
||
|
|
|
||
|
|
if err := ApplyRotateManageOptions(archive, current, RotateManageOptions{
|
||
|
|
Compress: true,
|
||
|
|
Pattern: "app.log.*",
|
||
|
|
}); err != nil {
|
||
|
|
t.Fatalf("ApplyRotateManageOptions compress failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if _, err := os.Stat(archive + ".gz"); err != nil {
|
||
|
|
t.Fatalf("compressed file should exist: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := os.Stat(archive); !os.IsNotExist(err) {
|
||
|
|
t.Fatalf("original archive file should be removed after compression")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestApplyRotateManageOptionsMaxBackups(t *testing.T) {
|
||
|
|
dir := testBinDir(t)
|
||
|
|
current := filepath.Join(dir, "app.log")
|
||
|
|
writeFileWithModTime(t, current, time.Now())
|
||
|
|
|
||
|
|
baseTime := time.Now().Add(-4 * time.Hour)
|
||
|
|
for i := 1; i <= 4; i++ {
|
||
|
|
path := filepath.Join(dir, "app.log."+strconv.Itoa(i))
|
||
|
|
writeFileWithModTime(t, path, baseTime.Add(time.Duration(i)*time.Hour))
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := ApplyRotateManageOptions(filepath.Join(dir, "app.log.4"), current, RotateManageOptions{
|
||
|
|
MaxBackups: 2,
|
||
|
|
Pattern: "app.log.*",
|
||
|
|
}); err != nil {
|
||
|
|
t.Fatalf("ApplyRotateManageOptions max backups failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
matches, err := filepath.Glob(filepath.Join(dir, "app.log.*"))
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("glob failed: %v", err)
|
||
|
|
}
|
||
|
|
if len(matches) != 2 {
|
||
|
|
t.Fatalf("should keep only 2 backup files, got %d (%v)", len(matches), matches)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestApplyRotateManageOptionsMaxAge(t *testing.T) {
|
||
|
|
dir := testBinDir(t)
|
||
|
|
current := filepath.Join(dir, "app.log")
|
||
|
|
oldBackup := filepath.Join(dir, "app.log.old")
|
||
|
|
newBackup := filepath.Join(dir, "app.log.new")
|
||
|
|
|
||
|
|
writeFileWithModTime(t, current, time.Now())
|
||
|
|
writeFileWithModTime(t, oldBackup, time.Now().Add(-4*time.Hour))
|
||
|
|
writeFileWithModTime(t, newBackup, time.Now().Add(-10*time.Minute))
|
||
|
|
|
||
|
|
if err := ApplyRotateManageOptions(newBackup, current, RotateManageOptions{
|
||
|
|
MaxAge: time.Hour,
|
||
|
|
Pattern: "app.log.*",
|
||
|
|
}); err != nil {
|
||
|
|
t.Fatalf("ApplyRotateManageOptions max age failed: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
if _, err := os.Stat(oldBackup); !os.IsNotExist(err) {
|
||
|
|
t.Fatalf("old backup should be removed by max age")
|
||
|
|
}
|
||
|
|
if _, err := os.Stat(newBackup); err != nil {
|
||
|
|
t.Fatalf("new backup should be kept: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestWithRotateManageOptionsKeepsPreviousHook(t *testing.T) {
|
||
|
|
archive := NewRotatePolicyArchive(&rotateWhenNonEmptyPolicy{}, 1)
|
||
|
|
var called bool
|
||
|
|
archive.SetHookAfterArchive(func(*StarLogger, string, string, os.FileInfo) error {
|
||
|
|
called = true
|
||
|
|
return nil
|
||
|
|
})
|
||
|
|
WithRotateManageOptions(archive, RotateManageOptions{})
|
||
|
|
|
||
|
|
dir := testBinDir(t)
|
||
|
|
current := filepath.Join(dir, "app.log")
|
||
|
|
archived := filepath.Join(dir, "app.log.1")
|
||
|
|
writeFileWithModTime(t, current, time.Now())
|
||
|
|
writeFileWithModTime(t, archived, time.Now())
|
||
|
|
|
||
|
|
hook := archive.HookAfterArchive()
|
||
|
|
if hook == nil {
|
||
|
|
t.Fatalf("hook after archive should not be nil")
|
||
|
|
}
|
||
|
|
if err := hook(nil, archived, current, nil); err != nil {
|
||
|
|
t.Fatalf("hook execution failed: %v", err)
|
||
|
|
}
|
||
|
|
if !called {
|
||
|
|
t.Fatalf("previous hook should still run after wrapping")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestIsManagedBackupNameDefaultSafeMatch(t *testing.T) {
|
||
|
|
base := "app.log"
|
||
|
|
stem := "app"
|
||
|
|
tests := []struct {
|
||
|
|
name string
|
||
|
|
matched bool
|
||
|
|
}{
|
||
|
|
{name: "app.log.20260318", matched: true},
|
||
|
|
{name: "app.log-1", matched: true},
|
||
|
|
{name: "app.log.1.gz", matched: true},
|
||
|
|
{name: "app.log.bak", matched: true},
|
||
|
|
{name: "app_20260318.log", matched: true},
|
||
|
|
{name: "app.log.current", matched: false},
|
||
|
|
{name: "app.log.backup", matched: false},
|
||
|
|
{name: "app-config.log", matched: false},
|
||
|
|
{name: "other.log.1", matched: false},
|
||
|
|
}
|
||
|
|
for _, tc := range tests {
|
||
|
|
matched, err := isManagedBackupName(tc.name, base, stem, "")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("isManagedBackupName(%q) returned err: %v", tc.name, err)
|
||
|
|
}
|
||
|
|
if matched != tc.matched {
|
||
|
|
t.Fatalf("isManagedBackupName(%q)=%v, want %v", tc.name, matched, tc.matched)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|