package diary_test import ( "bytes" "encoding/json" "os" "path/filepath" "strings" "testing" "time" "git.golem15.com/golem15/summercms/modules/bonfire" "git.golem15.com/golem15/summercms/modules/compass" "git.golem15.com/golem15/summercms/modules/diary" ) // earlyLogger is created before any Setup, the way modules create their // component loggers in package var blocks. var earlyLogger = diary.For("x") // TestDiaryTracerSmoke: one record from a component logger flows from // config/log.yaml through compass into a redacting stack of a console and a // daily file. func TestDiaryTracerSmoke(t *testing.T) { tmp := t.TempDir() configDir := filepath.Join(tmp, "config") if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatal(err) } logYAML := `default: stack level: info channels: stack: driver: stack channels: [console, daily] console: driver: console format: auto daily: driver: daily path: storage/logs/summer.log days: 14 format: json level: info ` if err := os.WriteFile(filepath.Join(configDir, "log.yaml"), []byte(logYAML), 0o644); err != nil { t.Fatal(err) } cfg, err := compass.Open(compass.Options{Dir: configDir, Environ: []string{}}) if err != nil { t.Fatal(err) } var buf bytes.Buffer now := func() time.Time { return time.Date(2026, 10, 21, 12, 0, 0, 0, time.Local) } t.Cleanup(diary.Reset) if err := diary.Setup(cfg, diary.Options{Root: tmp, Stderr: &buf, Color: bonfire.ColorNever, Now: now}); err != nil { t.Fatalf("Setup: %v", err) } diary.For("journal").Info("post saved", "password", "hunter2", "id", 7) earlyLogger.Info("early logger reached") console := buf.String() for _, want := range []string{"component=journal", "post saved", "[REDACTED]", "component=x", "early logger reached"} { if !strings.Contains(console, want) { t.Errorf("console output lacks %q:\n%s", want, console) } } if strings.Contains(console, "hunter2") { t.Errorf("console output leaks the password:\n%s", console) } if err := diary.Close(); err != nil { t.Fatalf("Close: %v", err) } file := filepath.Join(tmp, "storage", "logs", "summer-2026-10-21.log") info, err := os.Stat(file) if err != nil { t.Fatalf("daily file: %v", err) } if mode := info.Mode().Perm(); mode != 0o640 { t.Errorf("daily file mode = %o, want 640", mode) } raw, err := os.ReadFile(file) if err != nil { t.Fatal(err) } if bytes.Contains(raw, []byte("hunter2")) { t.Errorf("daily file leaks the password:\n%s", raw) } lines := strings.Split(strings.TrimSpace(string(raw)), "\n") if len(lines) != 2 { t.Fatalf("daily file has %d lines, want 2:\n%s", len(lines), raw) } var rec map[string]any if err := json.Unmarshal([]byte(lines[0]), &rec); err != nil { t.Fatalf("daily line is not JSON: %v\n%s", err, lines[0]) } if rec["component"] != "journal" || rec["password"] != "[REDACTED]" || rec["msg"] != "post saved" { t.Errorf("daily record = %v, want component journal, password [REDACTED], msg post saved", rec) } }