feat(acceptance): 增加生产同构媒体压力验收模式
引入动态流量门禁、隔离验收身份与协议级 Gemini/Volces 模拟器,覆盖双站点 API、Worker、PostgreSQL、River、账务、回调和媒体物化链路。 新增 P24/P28/P32 容量阶梯、Worker 强杀恢复、真实小流量 canary、CAS 放量和失败保持 validation 的生产编排;Worker 执行槽、连接池、媒体并发和双站点副本数改为环境配置。 验证:Go 全量测试、真实 PostgreSQL 迁移集成测试、迁移安全检查、OpenAPI 生成、ShellCheck、Kustomize、gofmt 和 git diff --check。
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
|
||||
)
|
||||
|
||||
func TestAcceptanceTrafficGateAndCASPromotion(t *testing.T) {
|
||||
databaseURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_DATABASE_URL"))
|
||||
if databaseURL == "" {
|
||||
t.Skip("set AI_GATEWAY_TEST_DATABASE_URL to run acceptance traffic integration test")
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
applyOIDCJITTestMigrations(t, ctx, databaseURL)
|
||||
db, err := Connect(ctx, databaseURL)
|
||||
if err != nil {
|
||||
t.Fatalf("connect store: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
resetTrafficMode := func() {
|
||||
_, _ = db.pool.Exec(context.Background(), `
|
||||
UPDATE system_settings
|
||||
SET value = '{"mode":"live","revision":0}'::jsonb, updated_at = now()
|
||||
WHERE setting_key = $1`, SystemSettingGatewayTrafficMode)
|
||||
}
|
||||
resetTrafficMode()
|
||||
t.Cleanup(resetTrafficMode)
|
||||
|
||||
token := "acceptance-token-" + time.Now().Format("150405.000000000")
|
||||
run, err := db.CreateAcceptanceRun(ctx, CreateAcceptanceRunInput{
|
||||
ReleaseSHA: strings.Repeat("a", 40),
|
||||
APIImageDigest: "sha256:" + strings.Repeat("b", 64),
|
||||
WorkerImageDigest: "sha256:" + strings.Repeat("c", 64),
|
||||
APIKeyID: "acceptance-api-key",
|
||||
UserID: "acceptance-user",
|
||||
Token: token,
|
||||
EmulatorBaseURL: "http://acceptance-emulator:8090",
|
||||
CallbackURL: "http://acceptance-emulator:8090/callbacks",
|
||||
CapacityProfile: "P24",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create acceptance run: %v", err)
|
||||
}
|
||||
encoded, _ := json.Marshal(run)
|
||||
if strings.Contains(string(encoded), token) {
|
||||
t.Fatal("acceptance run response exposed the raw token")
|
||||
}
|
||||
mode, err := db.ActivateAcceptanceRun(ctx, run.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("activate acceptance run: %v", err)
|
||||
}
|
||||
if mode.Mode != "validation" || mode.RunID != run.ID || mode.Revision != 1 {
|
||||
t.Fatalf("unexpected validation mode: %+v", mode)
|
||||
}
|
||||
user := &auth.User{ID: "acceptance-user", APIKeyID: "acceptance-api-key"}
|
||||
if _, err := db.AuthorizeAcceptanceTask(ctx, "", "", user); !errors.Is(err, ErrProductionTrafficPaused) {
|
||||
t.Fatalf("production request error=%v, want traffic paused", err)
|
||||
}
|
||||
if _, err := db.AuthorizeAcceptanceTask(ctx, run.ID, "wrong-token", user); !errors.Is(err, ErrAcceptanceNotAuthorized) {
|
||||
t.Fatalf("wrong acceptance token error=%v", err)
|
||||
}
|
||||
if authorizedRunID, err := db.AuthorizeAcceptanceTask(ctx, run.ID, token, user); err != nil || authorizedRunID != run.ID {
|
||||
t.Fatalf("authorize acceptance task run=%q err=%v", authorizedRunID, err)
|
||||
}
|
||||
if baseURL, credential, err := db.AcceptanceCandidateOverride(ctx, run.ID); err != nil ||
|
||||
baseURL != "http://acceptance-emulator:8090" || credential == "" || credential == token {
|
||||
t.Fatalf("candidate override base=%q credential=%q err=%v", baseURL, credential, err)
|
||||
}
|
||||
if _, err := db.FinishAcceptanceRun(ctx, FinishAcceptanceRunInput{
|
||||
RunID: run.ID, Passed: true, Report: map[string]any{"queueFinal": 0},
|
||||
}); err != nil {
|
||||
t.Fatalf("finish acceptance run: %v", err)
|
||||
}
|
||||
promotion := PromoteAcceptanceRunInput{
|
||||
RunID: run.ID, Revision: mode.Revision,
|
||||
ReleaseSHA: mode.ReleaseSHA, APIImageDigest: mode.APIImageDigest, WorkerImageDigest: mode.WorkerImageDigest,
|
||||
}
|
||||
stale := promotion
|
||||
stale.Revision++
|
||||
if _, err := db.PromoteAcceptanceRun(ctx, stale); !errors.Is(err, ErrAcceptanceStateConflict) {
|
||||
t.Fatalf("stale promotion error=%v, want state conflict", err)
|
||||
}
|
||||
live, err := db.PromoteAcceptanceRun(ctx, promotion)
|
||||
if err != nil {
|
||||
t.Fatalf("promote acceptance run: %v", err)
|
||||
}
|
||||
if live.Mode != "live" || live.Revision != 2 {
|
||||
t.Fatalf("unexpected live mode: %+v", live)
|
||||
}
|
||||
|
||||
failedRun, err := db.CreateAcceptanceRun(ctx, CreateAcceptanceRunInput{
|
||||
ReleaseSHA: strings.Repeat("d", 40),
|
||||
APIImageDigest: "sha256:" + strings.Repeat("e", 64),
|
||||
WorkerImageDigest: "sha256:" + strings.Repeat("f", 64),
|
||||
APIKeyID: "acceptance-api-key",
|
||||
UserID: "acceptance-user",
|
||||
Token: token + "-retry",
|
||||
EmulatorBaseURL: "http://acceptance-emulator:8090",
|
||||
CallbackURL: "http://acceptance-emulator:8090/callbacks",
|
||||
CapacityProfile: "P24",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create retry acceptance run: %v", err)
|
||||
}
|
||||
failedMode, err := db.ActivateAcceptanceRun(ctx, failedRun.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("activate retry acceptance run: %v", err)
|
||||
}
|
||||
if _, err := db.FinishAcceptanceRun(ctx, FinishAcceptanceRunInput{
|
||||
RunID: failedRun.ID, Passed: false, FailureReason: "load failed",
|
||||
}); err != nil {
|
||||
t.Fatalf("fail acceptance run: %v", err)
|
||||
}
|
||||
if retried, err := db.RetryAcceptanceRun(ctx, failedRun.ID); err != nil || retried.Status != "running" {
|
||||
t.Fatalf("retry acceptance run=%+v err=%v", retried, err)
|
||||
}
|
||||
aborted, err := db.AbortAcceptanceRun(ctx, PromoteAcceptanceRunInput{
|
||||
RunID: failedRun.ID, Revision: failedMode.Revision,
|
||||
ReleaseSHA: failedMode.ReleaseSHA, APIImageDigest: failedMode.APIImageDigest,
|
||||
WorkerImageDigest: failedMode.WorkerImageDigest,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("abort acceptance run: %v", err)
|
||||
}
|
||||
if aborted.Mode != "live" || aborted.Revision != failedMode.Revision+1 {
|
||||
t.Fatalf("unexpected aborted mode: %+v", aborted)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user