From 29a9b8c89fedd465caccdaf01ab0d00814b91078 Mon Sep 17 00:00:00 2001 From: wangbo Date: Fri, 31 Jul 2026 21:11:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(acceptance):=20=E4=BD=BF=E7=94=A8=E5=8F=AF?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E8=A7=A3=E7=A0=81=E7=9A=84=20WebP=20?= =?UTF-8?q?=E6=A0=B7=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 替换截断的 WebP 验收夹具,新增全量图片像素解码回归,并保留 6K 外部水化与三参考图归一化测试,避免协议模拟样本缺陷被误判为 Worker 运行故障。\n\n验证:Go 全量测试、go vet、gofmt、git diff --check 通过。 --- .../api/internal/acceptanceemulator/server.go | 12 +++-- .../acceptanceemulator/server_test.go | 25 ++++++++++- .../runner/input_image_normalizer_test.go | 45 +++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/apps/api/internal/acceptanceemulator/server.go b/apps/api/internal/acceptanceemulator/server.go index 7ab0fa1..fd578d0 100644 --- a/apps/api/internal/acceptanceemulator/server.go +++ b/apps/api/internal/acceptanceemulator/server.go @@ -712,10 +712,14 @@ func buildFixtures() map[string]fixture { _ = jpeg.Encode(&encoded, imageValue, &jpeg.Options{Quality: 88}) fixtures[fmt.Sprintf("image-%02d.jpg", index+4)] = fixture{ContentType: "image/jpeg", Payload: encoded.Bytes()} } - webpPayload, _ := base64.StdEncoding.DecodeString("UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEALmk0mk0iIiIiIgBoSygABc6zbAAA") - for index := 0; index < 4; index++ { - payload := append([]byte(nil), webpPayload...) - payload = append(payload, byte(index)) + webpFixtures := []string{ + "524946465a00000057454250565038204e000000f003009d012a400040003e9148a04c25a42322220800b012096900d3ca8000103b93c116da67710000feeea63fff80dd7c5b4cbfff7381ff7381ff7381fc6d4326178e9af219737c3e2990000000", + "5249464650000000574542505650382044000000d003009d012a400040003e9148a04c25a42322220800b012096900760000206ea6a00af10b720000feeed3dfffc5b9fb75d21ffff8b43ad0eb43447bcc63798400000000", + "52494646500000005745425056503820440000009003009d012a400040003e9148a04c25a42322220800b0120969000010685716c42e300a2000fef558bffffb9c0fffd9c0fffd9c0fe3afff5ea96b93a3f9696c1c000000", + "524946464e0000005745425056503820420000009003009d012a400040003e9148a04c25a42322220800b0120969000010375350057885b90000fef4351ffffba7afffda7afffda7afe60fff239de951800000000000", + } + for index, encoded := range webpFixtures { + payload, _ := hex.DecodeString(encoded) fixtures[fmt.Sprintf("image-%02d.webp", index+8)] = fixture{ContentType: "image/webp", Payload: payload} } for index := 0; index < 4; index++ { diff --git a/apps/api/internal/acceptanceemulator/server_test.go b/apps/api/internal/acceptanceemulator/server_test.go index 84655a5..f5cd4bb 100644 --- a/apps/api/internal/acceptanceemulator/server_test.go +++ b/apps/api/internal/acceptanceemulator/server_test.go @@ -62,11 +62,18 @@ func TestGeminiAndVolcesProtocolEmulation(t *testing.T) { if err != nil { t.Fatalf("get oversized fixture: %v", err) } - config, format, err := image.DecodeConfig(fixtureResponse.Body) + fixturePayload, readErr := io.ReadAll(fixtureResponse.Body) _ = fixtureResponse.Body.Close() + if readErr != nil { + t.Fatalf("read oversized fixture: %v", readErr) + } + config, format, err := image.DecodeConfig(bytes.NewReader(fixturePayload)) if err != nil || format != "jpeg" || config.Width != 6144 || config.Height != 2160 { t.Fatalf("oversized fixture format=%s config=%+v err=%v", format, config, err) } + if _, format, err = image.Decode(bytes.NewReader(fixturePayload)); err != nil || format != "jpeg" { + t.Fatalf("fully decode oversized fixture format=%s err=%v", format, err) + } content := []any{map[string]any{"type": "text", "text": "video"}} for index, name := range []string{"image-00.png", "image-04.jpg", "image-08.webp"} { @@ -148,6 +155,22 @@ func TestForcedConversionRejectsImagesOutsideOfficialSeedanceRange(t *testing.T) } } +func TestImageFixturesFullyDecode(t *testing.T) { + for name, fixture := range buildFixtures() { + if !strings.HasPrefix(fixture.ContentType, "image/") { + continue + } + decoded, format, err := image.Decode(bytes.NewReader(fixture.Payload)) + if err != nil { + t.Fatalf("fixture %s does not fully decode: %v", name, err) + } + bounds := decoded.Bounds() + if format == "" || bounds.Dx() <= 0 || bounds.Dy() <= 0 { + t.Fatalf("fixture %s format=%q bounds=%v", name, format, bounds) + } + } +} + func TestPaddedPNGIsValidAndExact(t *testing.T) { for _, size := range []int{256 << 10, 4 << 20, 8 << 20} { payload := paddedPNG(size) diff --git a/apps/api/internal/runner/input_image_normalizer_test.go b/apps/api/internal/runner/input_image_normalizer_test.go index b933269..b60d543 100644 --- a/apps/api/internal/runner/input_image_normalizer_test.go +++ b/apps/api/internal/runner/input_image_normalizer_test.go @@ -8,6 +8,7 @@ import ( "image/color" "image/jpeg" "image/png" + "os" "strings" "testing" "time" @@ -147,6 +148,50 @@ func TestImageNormalizationSlotSerializesHeavyConversions(t *testing.T) { } } +func TestHydrateAndNormalizeExternalSixKReference(t *testing.T) { + fixtureURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_VIDEO_FIXTURE_URL")) + if fixtureURL == "" { + t.Skip("set AI_GATEWAY_TEST_VIDEO_FIXTURE_URL to exercise a protocol fixture over HTTP") + } + service := &Service{imageNormalizeSlots: make(chan struct{}, 1)} + candidate := store.RuntimeModelCandidate{ + Provider: "volces", + PlatformConfig: map[string]any{"supportUrlInput": true, "supportBase64Input": true}, + ModelType: "omni_video", + Capabilities: map[string]any{ + "omni_video": map[string]any{ + "input_image_resolution_range": map[string]any{ + "min": map[string]any{"long_edge": float64(300), "short_edge": float64(300)}, + "max": map[string]any{"long_edge": float64(6000), "short_edge": float64(6000)}, + }, + "input_image_aspect_ratio_range": []any{float64(0.4), float64(2.5)}, + }, + }, + } + content := make([]any, 0, 3) + for range 3 { + content = append(content, map[string]any{ + "type": "image_url", "role": "reference_image", + "image_url": map[string]any{"url": fixtureURL}, + }) + } + body := map[string]any{"content": content} + hydrated, err := service.hydrateProviderRequestAssets(context.Background(), body, candidate) + if err != nil { + t.Fatal(err) + } + normalized, err := service.normalizeVideoInputImages(context.Background(), hydrated, candidate) + if err != nil { + t.Fatal(err) + } + for index, item := range normalized["content"].([]any) { + imageURL := item.(map[string]any)["image_url"].(map[string]any)["url"].(string) + if got := decodedImageSize(t, imageURL); got != image.Pt(6000, 2400) { + t.Fatalf("image %d has unexpected converted size: %v", index, got) + } + } +} + func TestNormalizeVideoInputImagesPadsAspectRatioViolation(t *testing.T) { service := &Service{} source := pngImageDataURL(t, 1530, 500)