package storage import ( "context" "log/slog" "testing" "time" ) func TestRunRetentionDeletesOldRecords(t *testing.T) { store := newTestStore(t) ctx := context.Background() logger := slog.Default() // Insert an old login attempt (200 days ago). oldTime := time.Now().AddDate(0, 0, -200).UTC().Format(time.RFC3339) _, err := store.db.Exec(` INSERT INTO login_attempts (username, password, ip, count, first_seen, last_seen) VALUES ('old', 'old', '1.1.1.1', 1, ?, ?)`, oldTime, oldTime) if err != nil { t.Fatalf("insert old attempt: %v", err) } // Insert a recent login attempt. if err := store.RecordLoginAttempt(ctx, "new", "new", "2.2.2.2", ""); err != nil { t.Fatalf("insert recent attempt: %v", err) } // Run retention with a short interval. Cancel immediately after first run. retentionCtx, cancel := context.WithCancel(ctx) done := make(chan struct{}) go func() { RunRetention(retentionCtx, store, 90, 24*time.Hour, logger) close(done) }() // Give it a moment to run the initial prune. time.Sleep(100 * time.Millisecond) cancel() <-done // Verify old record was deleted. var count int store.db.QueryRow(`SELECT COUNT(*) FROM login_attempts`).Scan(&count) if count != 1 { t.Errorf("remaining attempts = %d, want 1", count) } } func TestRunRetentionCancellation(t *testing.T) { store := newTestStore(t) logger := slog.Default() ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { RunRetention(ctx, store, 90, time.Millisecond, logger) close(done) }() // Cancel and verify it exits. cancel() select { case <-done: // OK case <-time.After(5 * time.Second): t.Fatal("RunRetention did not exit after cancel") } }