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:
2026-07-30 23:06:19 +08:00
parent f33d6d64e0
commit 3053ba4925
45 changed files with 5214 additions and 45 deletions
@@ -0,0 +1,186 @@
package acceptanceemulator
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"image"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
)
func TestGeminiAndVolcesProtocolEmulation(t *testing.T) {
var mu sync.Mutex
now := time.Unix(1_800_000_000, 0)
server := New(Config{
Now: func() time.Time {
mu.Lock()
defer mu.Unlock()
return now
},
Wait: func(context.Context, time.Duration) error { return nil },
})
httpServer := httptest.NewServer(server.Handler())
defer httpServer.Close()
input := paddedPNG(256 << 10)
geminiBody, _ := json.Marshal(map[string]any{
"contents": []any{map[string]any{"parts": []any{
map[string]any{"text": "edit image"},
map[string]any{"inlineData": map[string]any{
"mimeType": "image/png", "data": base64.StdEncoding.EncodeToString(input),
}},
}}},
"generationConfig": map[string]any{"responseModalities": []any{"IMAGE"}},
})
response, err := http.Post(httpServer.URL+"/v1beta/models/gemini-test:generateContent", "application/json", bytes.NewReader(geminiBody))
if err != nil {
t.Fatalf("Gemini request: %v", err)
}
if response.StatusCode != http.StatusOK {
payload, _ := io.ReadAll(response.Body)
t.Fatalf("Gemini status=%d body=%s", response.StatusCode, payload)
}
var geminiResult map[string]any
if err := json.NewDecoder(response.Body).Decode(&geminiResult); err != nil {
t.Fatalf("decode Gemini response: %v", err)
}
_ = response.Body.Close()
fixtureResponse, err := http.Get(httpServer.URL + "/fixtures/image-12-4k.jpg")
if err != nil {
t.Fatalf("get 4K fixture: %v", err)
}
config, format, err := image.DecodeConfig(fixtureResponse.Body)
_ = fixtureResponse.Body.Close()
if err != nil || format != "jpeg" || config.Width != 4096 || config.Height != 2160 {
t.Fatalf("4K fixture format=%s config=%+v err=%v", format, config, err)
}
content := []any{map[string]any{"type": "text", "text": "video"}}
for index, name := range []string{"image-00.png", "image-04.jpg", "image-08.webp"} {
role := "reference_image"
if index == 0 {
role = "first_frame"
}
content = append(content, map[string]any{
"type": "image_url", "role": role,
"image_url": map[string]any{"url": httpServer.URL + "/fixtures/" + name},
})
}
videoBody, _ := json.Marshal(map[string]any{"model": "seedance-test", "content": content, "seed": 1})
response, err = http.Post(httpServer.URL+"/contents/generations/tasks", "application/json", bytes.NewReader(videoBody))
if err != nil {
t.Fatalf("submit video: %v", err)
}
if response.StatusCode != http.StatusOK {
t.Fatalf("submit video status=%d", response.StatusCode)
}
var submitted map[string]any
_ = json.NewDecoder(response.Body).Decode(&submitted)
_ = response.Body.Close()
taskID := strings.TrimSpace(stringValue(submitted["id"]))
if taskID == "" {
t.Fatal("video emulator returned no task ID")
}
mu.Lock()
now = now.Add(20 * time.Second)
mu.Unlock()
response, err = http.Get(httpServer.URL + "/contents/generations/tasks/" + taskID)
if err != nil {
t.Fatalf("poll video: %v", err)
}
var polled map[string]any
_ = json.NewDecoder(response.Body).Decode(&polled)
_ = response.Body.Close()
if polled["status"] != "succeeded" {
t.Fatalf("video status=%v", polled["status"])
}
response, err = http.Get(httpServer.URL + "/report")
if err != nil {
t.Fatalf("get report: %v", err)
}
var report Report
_ = json.NewDecoder(response.Body).Decode(&report)
_ = response.Body.Close()
if report.GeminiRequests != 1 || report.VideoSubmissions != 1 || report.VideoInvalid != 0 ||
report.VideoReferenceCounts["3"] != 1 || report.UniqueImageHashes != 3 {
t.Fatalf("unexpected emulator report: %+v", report)
}
}
func TestPaddedPNGIsValidAndExact(t *testing.T) {
for _, size := range []int{256 << 10, 4 << 20, 8 << 20} {
payload := paddedPNG(size)
if len(payload) != size {
t.Fatalf("PNG bytes=%d, want=%d", len(payload), size)
}
if _, _, err := image.DecodeConfig(bytes.NewReader(payload)); err != nil {
t.Fatalf("decode padded PNG: %v", err)
}
}
}
func TestScaledDelayStaysOnWholeSecondsWithinProfile(t *testing.T) {
delay := scaledDelay(8*time.Second, 15*time.Second, 2<<20)
if delay < 8*time.Second || delay > 15*time.Second || delay%time.Second != 0 {
t.Fatalf("scaled delay=%s", delay)
}
}
func TestVolcesProtocolAcceptsThreeSixAndNineReferenceImages(t *testing.T) {
server := New(Config{Wait: func(context.Context, time.Duration) error { return nil }})
httpServer := httptest.NewServer(server.Handler())
defer httpServer.Close()
for _, imageCount := range []int{3, 6, 9} {
content := []any{map[string]any{"type": "text", "text": "multi-reference video"}}
for index := 0; index < imageCount; index++ {
role := "reference_image"
if index == 0 {
role = "first_frame"
}
if imageCount > 3 && index == imageCount-1 {
role = "last_frame"
}
content = append(content, map[string]any{
"type": "image_url", "role": role,
"image_url": map[string]any{
"url": fmt.Sprintf("%s/fixtures/image-%02d.png", httpServer.URL, index%4),
},
})
}
body, _ := json.Marshal(map[string]any{"model": "seedance-test", "content": content, "seed": imageCount})
response, err := http.Post(httpServer.URL+"/contents/generations/tasks", "application/json", bytes.NewReader(body))
if err != nil {
t.Fatalf("%d-image submit: %v", imageCount, err)
}
_ = response.Body.Close()
if response.StatusCode != http.StatusOK {
t.Fatalf("%d-image status=%d", imageCount, response.StatusCode)
}
}
response, err := http.Get(httpServer.URL + "/report")
if err != nil {
t.Fatalf("get report: %v", err)
}
defer response.Body.Close()
var report Report
if err := json.NewDecoder(response.Body).Decode(&report); err != nil {
t.Fatalf("decode report: %v", err)
}
for _, imageCount := range []string{"3", "6", "9"} {
if report.VideoReferenceCounts[imageCount] != 1 {
t.Fatalf("reference counts=%v", report.VideoReferenceCounts)
}
}
}