refactor(runner): 收敛上游失败决策与轮转策略
将同平台重试、跨平台动作、健康副作用和冷却排队统一到单一失败决策,避免旧降级策略与 failover 重复执行。\n\n增加事务级单源保护、候选实时刷新、冷却错误契约、管理接口严格校验及兼容策略只读展示。\n\n验证:真实 Gateway HTTP/PostgreSQL 接受测试 10 项通过;go test ./...、pnpm openapi、pnpm lint、pnpm test、pnpm build 均通过。
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||
)
|
||||
|
||||
func TestModelCooldownErrorPublicContract(t *testing.T) {
|
||||
recoveryAt := time.Now().UTC().Add(17 * time.Second).Truncate(time.Second)
|
||||
err := &store.ModelCandidateUnavailableError{
|
||||
Code: "model_cooling_down",
|
||||
Message: "请求的模型暂时不可用,请稍后重试",
|
||||
RetryAfter: 17 * time.Second,
|
||||
RecoveryAt: recoveryAt,
|
||||
Details: map[string]any{
|
||||
"retryAfterSeconds": 17,
|
||||
"recoveryAt": recoveryAt.Format(time.RFC3339),
|
||||
},
|
||||
}
|
||||
recorder := httptest.NewRecorder()
|
||||
applyRunErrorHeaders(recorder, err)
|
||||
|
||||
if got := statusFromRunError(err); got != 429 {
|
||||
t.Fatalf("status = %d, want 429", got)
|
||||
}
|
||||
if got := runErrorCode(err); got != "model_cooling_down" {
|
||||
t.Fatalf("code = %q, want model_cooling_down", got)
|
||||
}
|
||||
if got := recorder.Header().Get("Retry-After"); got != "17" {
|
||||
t.Fatalf("Retry-After = %q, want 17", got)
|
||||
}
|
||||
details := runErrorDetails(err)
|
||||
if len(details) != 2 || details["retryAfterSeconds"] != 17 || details["recoveryAt"] != recoveryAt.Format(time.RFC3339) {
|
||||
t.Fatalf("unexpected public details: %#v", details)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user