package analytics import ( "os" "path/filepath" "runtime" "sync" "testing" ) func TestDaySaltIsStableAndRotates(t *testing.T) { dir := t.TempDir() a, err := DaySalt(dir, "2026-09-07") if err != nil || len(a) != 32 { t.Fatalf("salt: %v len=%d", err, len(a)) } b, _ := DaySalt(dir, "2026-09-07") if string(a) != string(b) { t.Fatal("same day must return the same salt (restart-stable)") } if runtime.GOOS != "windows" { st, _ := os.Stat(filepath.Join(dir, "analytics-salt-2026-09-07")) if st.Mode().Perm() != 0o600 { t.Fatalf("mode %v", st.Mode().Perm()) } } c, _ := DaySalt(dir, "2026-09-08") if string(c) == string(a) { t.Fatal("a new day must salt afresh") } if _, err := os.Stat(filepath.Join(dir, "analytics-salt-2026-09-07")); !os.IsNotExist(err) { t.Fatal("rotation must delete the previous day's salt") } } func TestDaySaltConcurrentCallersAgree(t *testing.T) { dir := t.TempDir() const n = 8 results := make([][]byte, n) errs := make([]error, n) var wg sync.WaitGroup wg.Add(n) for i := 0; i < n; i++ { i := i go func() { defer wg.Done() results[i], errs[i] = DaySalt(dir, "2026-09-07") }() } wg.Wait() for i, err := range errs { if err != nil { t.Fatalf("goroutine %d: %v", i, err) } if len(results[i]) != 32 { t.Fatalf("goroutine %d: len=%d", i, len(results[i])) } } for i := 1; i < n; i++ { if string(results[i]) != string(results[0]) { t.Fatalf("goroutine %d's salt disagrees with goroutine 0's", i) } } }