package config_test import ( "os" "path/filepath" "testing" "time" "workshop.perforce.com/p4lf/internal/config" ) func writeConfig(t *testing.T, content string) string { t.Helper() f, err := os.CreateTemp(t.TempDir(), "p4lf-*.cfg") if err != nil { t.Fatalf("creating temp config: %v", err) } f.WriteString(content) f.Close() return f.Name() } func TestLoad_Defaults(t *testing.T) { path := writeConfig(t, "P4LogFile = /p4/1/logs/log\n") cfg, err := config.Load(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.LogTailDelay != 60*time.Second { t.Errorf("LogTailDelay: got %v, want 60s", cfg.LogTailDelay) } if cfg.MaxLogChunks != 0 { t.Errorf("MaxLogChunks: got %d, want 0", cfg.MaxLogChunks) } if !cfg.ReadFromStart { t.Error("ReadFromStart: want true by default") } if cfg.Debug != 0 { t.Errorf("Debug: got %d, want 0", cfg.Debug) } } func TestLoad_AllSettings(t *testing.T) { path := writeConfig(t, ` P4LogFile = /p4/1/logs/log LogTailDelay = 30s MaxLogChunks = 10 MinLogSpace = 5% MaxLogSize = 50M MaxRotationRecoverySize = 200M StateFile = /tmp/p4lf.state ReadFromStart = false Debug = 2 `) cfg, err := config.Load(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.P4LogFile != "/p4/1/logs/log" { t.Errorf("P4LogFile: got %q", cfg.P4LogFile) } if cfg.LogTailDelay != 30*time.Second { t.Errorf("LogTailDelay: got %v, want 30s", cfg.LogTailDelay) } if cfg.MaxLogChunks != 10 { t.Errorf("MaxLogChunks: got %d, want 10", cfg.MaxLogChunks) } if !cfg.MinLogSpace.IsPercent || cfg.MinLogSpace.Percent != 5 { t.Errorf("MinLogSpace: got %+v, want 5%%", cfg.MinLogSpace) } if cfg.MaxLogSize != 50*1024*1024 { t.Errorf("MaxLogSize: got %d, want %d", cfg.MaxLogSize, 50*1024*1024) } if cfg.MaxRotationRecoverySize != 200*1024*1024 { t.Errorf("MaxRotationRecoverySize: got %d, want %d", cfg.MaxRotationRecoverySize, 200*1024*1024) } if cfg.StateFile != "/tmp/p4lf.state" { t.Errorf("StateFile: got %q", cfg.StateFile) } if cfg.ReadFromStart { t.Error("ReadFromStart: want false") } if cfg.Debug != 2 { t.Errorf("Debug: got %d, want 2", cfg.Debug) } } func TestLoad_DurationFormats(t *testing.T) { cases := []struct { input string want time.Duration }{ {"60s", 60 * time.Second}, {"3m", 3 * time.Minute}, {"1h", 1 * time.Hour}, {"120", 120 * time.Second}, } for _, tc := range cases { path := writeConfig(t, "P4LogFile = /log\nLogTailDelay = "+tc.input+"\n") cfg, err := config.Load(path) if err != nil { t.Errorf("input %q: unexpected error: %v", tc.input, err) continue } if cfg.LogTailDelay != tc.want { t.Errorf("input %q: got %v, want %v", tc.input, cfg.LogTailDelay, tc.want) } } } func TestLoad_SizeFormats(t *testing.T) { cases := []struct { input string want int64 }{ {"100M", 100 * 1024 * 1024}, {"2G", 2 * 1024 * 1024 * 1024}, {"1T", 1024 * 1024 * 1024 * 1024}, {"512K", 512 * 1024}, {"0", 0}, {"None", 0}, } for _, tc := range cases { path := writeConfig(t, "P4LogFile = /log\nMaxLogSize = "+tc.input+"\n") cfg, err := config.Load(path) if err != nil { t.Errorf("input %q: unexpected error: %v", tc.input, err) continue } if cfg.MaxLogSize != tc.want { t.Errorf("input %q: got %d, want %d", tc.input, cfg.MaxLogSize, tc.want) } } } func TestLoad_SpaceSpecAbsolute(t *testing.T) { path := writeConfig(t, "P4LogFile = /log\nMinLogSpace = 3G\n") cfg, err := config.Load(path) if err != nil { t.Fatalf("unexpected error: %v", err) } if cfg.MinLogSpace.IsPercent { t.Error("expected absolute size, got percent") } if cfg.MinLogSpace.Bytes != 3*1024*1024*1024 { t.Errorf("Bytes: got %d, want %d", cfg.MinLogSpace.Bytes, 3*1024*1024*1024) } } func TestLoad_MissingP4LogFile(t *testing.T) { // Temporarily unset P4LOG if set. old := os.Getenv("P4LOG") os.Unsetenv("P4LOG") defer os.Setenv("P4LOG", old) path := writeConfig(t, "# no P4LogFile set\n") _, err := config.Load(path) if err == nil { t.Error("expected error for missing P4LogFile, got nil") } } func TestLoad_UnknownKey(t *testing.T) { path := writeConfig(t, "P4LogFile = /log\nUnknownKey = value\n") _, err := config.Load(path) if err == nil { t.Error("expected error for unknown key, got nil") } } func TestLoad_Comments(t *testing.T) { path := writeConfig(t, `# This is a comment P4LogFile = /p4/1/logs/log # LogTailDelay = 999s `) cfg, err := config.Load(path) if err != nil { t.Fatalf("unexpected error: %v", err) } // LogTailDelay should be default (60s), not 999s. if cfg.LogTailDelay != 60*time.Second { t.Errorf("commented-out key was applied: LogTailDelay = %v", cfg.LogTailDelay) } } func TestLoad_MissingFile(t *testing.T) { _, err := config.Load(filepath.Join(t.TempDir(), "nonexistent.cfg")) if err == nil { t.Error("expected error for missing config file, got nil") } }