将同平台重试、跨平台动作、健康副作用和冷却排队统一到单一失败决策,避免旧降级策略与 failover 重复执行。\n\n增加事务级单源保护、候选实时刷新、冷却错误契约、管理接口严格校验及兼容策略只读展示。\n\n验证:真实 Gateway HTTP/PostgreSQL 接受测试 10 项通过;go test ./...、pnpm openapi、pnpm lint、pnpm test、pnpm build 均通过。
58 lines
2.1 KiB
Go
58 lines
2.1 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|