package runner import ( "bytes" "context" "encoding/base64" "image" "image/color" "image/jpeg" "image/png" "strings" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/clients" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func seedanceImageConstraintCandidate() store.RuntimeModelCandidate { return store.RuntimeModelCandidate{ ModelType: "image_to_video", Capabilities: map[string]any{ "image_to_video": map[string]any{ "input_image_resolution_range": map[string]any{ "min": map[string]any{"long_edge": float64(360), "short_edge": float64(360)}, "max": map[string]any{"long_edge": float64(1920), "short_edge": float64(1080)}, }, "input_image_aspect_ratio_range": []any{float64(0.39), float64(2.5)}, }, }, } } func pngImageDataURL(t *testing.T, width int, height int) string { t.Helper() source := image.NewRGBA(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { for x := 0; x < width; x++ { source.Set(x, y, color.RGBA{R: 80, G: 120, B: 160, A: 255}) } } var payload bytes.Buffer if err := png.Encode(&payload, source); err != nil { t.Fatal(err) } return "data:image/png;base64," + base64.StdEncoding.EncodeToString(payload.Bytes()) } func decodedImageSize(t *testing.T, source string) image.Point { t.Helper() if !strings.HasPrefix(source, "data:image/jpeg;base64,") { t.Fatalf("expected converted JPEG data URL, got prefix %.32q", source) } payload, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(source, "data:image/jpeg;base64,")) if err != nil { t.Fatal(err) } config, err := jpeg.DecodeConfig(bytes.NewReader(payload)) if err != nil { t.Fatal(err) } return image.Pt(config.Width, config.Height) } func TestNormalizeVideoInputImagesResizesOversizedSeedanceImage(t *testing.T) { service := &Service{} body := map[string]any{ "content": []any{ map[string]any{ "type": "image_url", "role": "first_frame", "image_url": map[string]any{"url": pngImageDataURL(t, 3840, 2160)}, }, }, } normalized, err := service.normalizeVideoInputImages(context.Background(), body, seedanceImageConstraintCandidate()) if err != nil { t.Fatal(err) } content := normalized["content"].([]any) item := content[0].(map[string]any) imageURL := item["image_url"].(map[string]any)["url"].(string) if got := decodedImageSize(t, imageURL); got != image.Pt(1920, 1080) { t.Fatalf("unexpected converted size: %v", got) } } func TestNormalizeVideoInputImagesPadsAspectRatioViolation(t *testing.T) { service := &Service{} source := pngImageDataURL(t, 1530, 500) body := map[string]any{"first_frame": source} normalized, err := service.normalizeVideoInputImages(context.Background(), body, seedanceImageConstraintCandidate()) if err != nil { t.Fatal(err) } if got := decodedImageSize(t, normalized["first_frame"].(string)); got != image.Pt(1530, 612) { t.Fatalf("unexpected converted size: %v", got) } } func TestNormalizeVideoInputImagesUpscalesBelowMinimumResolution(t *testing.T) { service := &Service{} body := map[string]any{"first_frame": pngImageDataURL(t, 200, 100)} normalized, err := service.normalizeVideoInputImages(context.Background(), body, seedanceImageConstraintCandidate()) if err != nil { t.Fatal(err) } if got := decodedImageSize(t, normalized["first_frame"].(string)); got != image.Pt(720, 360) { t.Fatalf("unexpected converted size: %v", got) } } func TestNormalizeVideoInputImagesKeepsCompliantImage(t *testing.T) { service := &Service{} source := pngImageDataURL(t, 1280, 720) body := map[string]any{"first_frame": source} normalized, err := service.normalizeVideoInputImages(context.Background(), body, seedanceImageConstraintCandidate()) if err != nil { t.Fatal(err) } if normalized["first_frame"] != source { t.Fatal("compliant image should remain unchanged") } } func TestNormalizeVideoInputImagesReturnsActionableDecodeError(t *testing.T) { service := &Service{} body := map[string]any{"first_frame": "data:image/png;base64,bm90LWltYWdl"} _, err := service.normalizeVideoInputImages(context.Background(), body, seedanceImageConstraintCandidate()) if err == nil { t.Fatal("expected decode error") } if clients.ErrorCode(err) != "invalid_parameter" || clients.ErrorParam(err) != "first_frame" { t.Fatalf("unexpected error contract: code=%s param=%s err=%v", clients.ErrorCode(err), clients.ErrorParam(err), err) } }