package httpapi import ( "strings" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestValidateRunnerPolicyInputActionRules(t *testing.T) { validRule := func() map[string]any { return map[string]any{ "categories": []any{"rate_limit"}, "action": "cooldown_and_next", "target": "model", "cooldownSeconds": float64(60), } } tests := []struct { name string mutate func(map[string]any) wantErr string }{ {name: "valid"}, {name: "invalid action", mutate: func(rule map[string]any) { rule["action"] = "rotate" }, wantErr: ".action is invalid"}, {name: "invalid target", mutate: func(rule map[string]any) { rule["target"] = "client" }, wantErr: ".target is invalid"}, {name: "empty match", mutate: func(rule map[string]any) { rule["categories"] = []any{} }, wantErr: "at least one match condition"}, {name: "missing cooldown", mutate: func(rule map[string]any) { delete(rule, "cooldownSeconds") }, wantErr: ".cooldownSeconds must be a positive integer"}, {name: "zero cooldown", mutate: func(rule map[string]any) { rule["cooldownSeconds"] = float64(0) }, wantErr: ".cooldownSeconds must be a positive integer"}, {name: "fractional cooldown", mutate: func(rule map[string]any) { rule["cooldownSeconds"] = 1.5 }, wantErr: ".cooldownSeconds must be a positive integer"}, {name: "missing demote steps", mutate: func(rule map[string]any) { rule["action"] = "demote_and_next" delete(rule, "cooldownSeconds") }, wantErr: ".demoteSteps must be a positive integer"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { rule := validRule() if test.mutate != nil { test.mutate(rule) } err := validateRunnerPolicyInput(store.RunnerPolicyInput{ FailoverPolicy: map[string]any{"actionRules": []any{rule}}, }) if test.wantErr == "" { if err != nil { t.Fatalf("unexpected validation error: %v", err) } return } if err == nil || !strings.Contains(err.Error(), test.wantErr) { t.Fatalf("validation error = %v, want substring %q", err, test.wantErr) } }) } }