package maintenance import ( "context" "errors" "strings" "testing" "evobgp/internal/store" ) func validPolicy() *store.MaintenancePolicy { ret := 86400 return &store.MaintenancePolicy{ ID: "11111111-1111-1111-1111-111111111111", Name: "job audit", TableName: "job_audit", Condition: "true", RetentionPeriodSec: &ret, VacuumStrategy: store.VacuumStrategyNone, Schedule: "0 3 * * *", Enabled: true, } } func TestPolicyExecutorExecuteValidation(t *testing.T) { ctx := context.Background() mem := store.NewMemory() base := validPolicy() tests := []struct { name string exec *PolicyExecutor policy *store.MaintenancePolicy wantErr error contains string }{ { name: "nil policy", exec: &PolicyExecutor{Store: mem}, policy: nil, wantErr: store.ErrInvalidInput, }, { name: "nil pool", exec: &PolicyExecutor{Store: mem, Pool: nil}, policy: base, contains: "postgres not configured", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { _, err := tc.exec.Execute(ctx, tc.policy, false) if tc.wantErr != nil { if !errors.Is(err, tc.wantErr) { t.Fatalf("Execute() err=%v want %v", err, tc.wantErr) } return } if err == nil { t.Fatal("Execute() expected error") } if tc.contains != "" && !strings.Contains(err.Error(), tc.contains) { t.Fatalf("Execute() err=%q want substring %q", err, tc.contains) } }) } } func TestPolicyExecutorPreExecuteValidation(t *testing.T) { p := validPolicy() p.TableName = "tenant" if err := ValidateTableName(p.TableName); err == nil { t.Fatal("expected blocked table error") } p = validPolicy() p.Condition = "1=1; DROP TABLE job_audit" if err := ValidateCondition(p.Condition); err == nil { t.Fatal("expected unsafe condition error") } p = validPolicy() p.VacuumStrategy = "invalid" if !store.ValidVacuumStrategy(p.VacuumStrategy) { return } t.Fatal("expected invalid vacuum strategy") } func TestPolicyAction(t *testing.T) { ret := 3600 tests := []struct { name string p *store.MaintenancePolicy want string }{ {"nil", nil, "run"}, {"cleanup only", &store.MaintenancePolicy{RetentionPeriodSec: &ret, VacuumStrategy: store.VacuumStrategyNone}, "cleanup"}, {"vacuum only", &store.MaintenancePolicy{VacuumStrategy: store.VacuumStrategyVacuum}, "vacuum"}, {"cleanup+vacuum", &store.MaintenancePolicy{MaxRows: &ret, VacuumStrategy: store.VacuumStrategyAnalyze}, "cleanup_vacuum"}, {"noop run", &store.MaintenancePolicy{VacuumStrategy: store.VacuumStrategyNone}, "run"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := policyAction(tc.p); got != tc.want { t.Fatalf("policyAction()=%q want %q", got, tc.want) } }) } } func TestVacuumKind(t *testing.T) { tests := []struct { strategy string want string }{ {store.VacuumStrategyVacuum, "vacuum"}, {store.VacuumStrategyAnalyze, "analyze"}, {store.VacuumStrategyVacuumAnalyze, "vacuum_analyze"}, {store.VacuumStrategyReindex, "reindex"}, {"unknown", "vacuum"}, } for _, tc := range tests { if got := vacuumKind(tc.strategy); got != tc.want { t.Fatalf("vacuumKind(%q)=%q want %q", tc.strategy, got, tc.want) } } } func TestRowsDeletedFromDetail(t *testing.T) { tests := []struct { name string detail map[string]any want int64 }{ {"nil", nil, 0}, {"int64", map[string]any{"deleted": int64(42)}, 42}, {"int", map[string]any{"deleted": 7}, 7}, {"float64", map[string]any{"deleted": float64(3)}, 3}, {"missing", map[string]any{"other": 1}, 0}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if got := rowsDeletedFromDetail(tc.detail); got != tc.want { t.Fatalf("rowsDeletedFromDetail()=%d want %d", got, tc.want) } }) } } func TestAdvisoryKeyStable(t *testing.T) { a := advisoryKey("policy-a") b := advisoryKey("policy-a") c := advisoryKey("policy-b") if a != b { t.Fatal("advisory key not stable for same id") } if a == c { t.Fatal("advisory key collision for different ids") } }