package audit import ( "context" "os" "testing" "time" "github.com/jackc/pgx/v5/pgxpool" ) func TestMaintenancePartitionsRetentionAndIdempotency(t *testing.T) { databaseURL := os.Getenv("AUDIT_MAINTENANCE_TEST_DATABASE_URL") if databaseURL == "" { t.Skip("AUDIT_MAINTENANCE_TEST_DATABASE_URL is not configured") } ctx := context.Background() pool, err := pgxpool.New(ctx, databaseURL) if err != nil { t.Fatal(err) } defer pool.Close() const oldID = "44444444-4444-4444-8444-444444444444" const currentID = "55555555-5555-4555-8555-555555555555" _, err = pool.Exec(ctx, `INSERT INTO gateway.audit_events(id,request_id,protocol,status_code,recorded_at) VALUES ($1,'maintenance-old','/v1/test',200,'2026-01-15T00:00:00Z'), ($2,'maintenance-current','/v1/test',200,'2026-08-10T00:00:00Z')`, oldID, currentID) if err != nil { t.Fatal(err) } now := time.Date(2026, 8, 11, 0, 0, 0, 0, time.UTC) first, err := NewMaintenance(pool, 300*24*time.Hour, 730*24*time.Hour, 1).Run(ctx, now) if err != nil { t.Fatal(err) } if !contains(first.CreatedPartitions, "audit_events_202601") || !contains(first.CreatedPartitions, "audit_events_202608") { t.Fatalf("expected old and current partitions, got %#v", first.CreatedPartitions) } second, err := NewMaintenance(pool, 90*24*time.Hour, 730*24*time.Hour, 1).Run(ctx, now) if err != nil { t.Fatal(err) } if !contains(second.DroppedPartitions, "audit_events_202601") { t.Fatalf("expected stale partition to be dropped, got %#v", second.DroppedPartitions) } var currentTable string if err := pool.QueryRow(ctx, `SELECT tableoid::regclass::text FROM gateway.audit_events WHERE id=$1`, currentID).Scan(¤tTable); err != nil { t.Fatal(err) } if currentTable != "gateway.audit_events_202608" && currentTable != "audit_events_202608" { t.Fatalf("current row was not routed to monthly partition: %s", currentTable) } var oldCount int if err := pool.QueryRow(ctx, `SELECT count(*) FROM gateway.audit_events WHERE id=$1`, oldID).Scan(&oldCount); err != nil || oldCount != 0 { t.Fatalf("expired row still exists: count=%d err=%v", oldCount, err) } third, err := NewMaintenance(pool, 90*24*time.Hour, 730*24*time.Hour, 1).Run(ctx, now) if err != nil { t.Fatal(err) } if len(third.CreatedPartitions) != 0 || len(third.DroppedPartitions) != 0 { t.Fatalf("maintenance must be idempotent: %#v", third) } } func contains(values []string, wanted string) bool { for _, value := range values { if value == wanted { return true } } return false }