package runner import ( "bytes" "context" "encoding/base64" "os" "path/filepath" "strings" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestGeneratedAssetDecisionSkipsURLResultAndStripsInlinePayload(t *testing.T) { item := map[string]any{ "b64_json": base64.StdEncoding.EncodeToString([]byte("inline image")), "url": "https://cdn.example.com/generated.png", } decision, err := generatedAssetDecisionForItem("images.generations", item, defaultGeneratedAssetUploadPolicy()) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.Inline != nil { t.Fatalf("URL media should not be uploaded by the default policy") } if !containsString(decision.StripKeys, "b64_json") { t.Fatalf("inline payload should be stripped when URL is already available: %+v", decision.StripKeys) } } func TestGeneratedAssetDecisionUploadsInlineImageBase64(t *testing.T) { item := map[string]any{ "b64_json": base64.StdEncoding.EncodeToString([]byte("inline image")), "mime_type": "image/jpeg", } decision, err := generatedAssetDecisionForItem("images.generations", item, defaultGeneratedAssetUploadPolicy()) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.Inline == nil { t.Fatalf("expected inline image to be uploaded") } if decision.Inline.Kind != "image" || decision.Inline.ContentType != "image/jpeg" { t.Fatalf("unexpected inline image metadata: %+v", decision.Inline) } if !containsString(decision.StripKeys, "b64_json") { t.Fatalf("uploaded inline payload should be stripped: %+v", decision.StripKeys) } } func TestGeneratedAssetDecisionUploadsInlineVideoBuffer(t *testing.T) { item := map[string]any{ "type": "video", "video_buffer": []any{float64(0), float64(1), float64(2), float64(3)}, } decision, err := generatedAssetDecisionForItem("videos.generations", item, defaultGeneratedAssetUploadPolicy()) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.Inline == nil { t.Fatalf("expected inline video buffer to be uploaded") } if decision.Inline.Kind != "video" || decision.Inline.ContentType != "video/mp4" { t.Fatalf("unexpected inline video metadata: %+v", decision.Inline) } if !containsString(decision.StripKeys, "video_buffer") { t.Fatalf("uploaded video buffer should be stripped: %+v", decision.StripKeys) } } func TestGeneratedAssetDecisionUploadsDataURL(t *testing.T) { item := map[string]any{ "url": "data:image/webp;base64," + base64.StdEncoding.EncodeToString([]byte("inline webp")), } decision, err := generatedAssetDecisionForItem("images.generations", item, defaultGeneratedAssetUploadPolicy()) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.Inline == nil { t.Fatalf("expected data URL to be uploaded") } if decision.Inline.SourceKey != "url" || decision.Inline.ContentType != "image/webp" { t.Fatalf("unexpected data URL metadata: %+v", decision.Inline) } if !containsString(decision.StripKeys, "url") { t.Fatalf("uploaded data URL field should be stripped: %+v", decision.StripKeys) } } func TestGeneratedAssetDecisionUploadsURLWhenPolicyUploadAll(t *testing.T) { item := map[string]any{ "type": "video", "video_url": "https://cdn.example.com/generated.mp4", } decision, err := generatedAssetDecisionForItem("videos.generations", item, generatedAssetUploadPolicy{UploadInlineMedia: true, UploadURLMedia: true}) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.URL == nil { t.Fatalf("expected URL media to be uploaded") } if decision.URL.Kind != "video" || decision.URL.SourceKey != "video_url" { t.Fatalf("unexpected URL media metadata: %+v", decision.URL) } if !containsString(decision.StripKeys, "video_url") { t.Fatalf("uploaded URL field should be stripped: %+v", decision.StripKeys) } } func TestGeneratedAssetDecisionStoresInlineLocallyWhenPolicyUploadNone(t *testing.T) { item := map[string]any{ "b64_json": base64.StdEncoding.EncodeToString([]byte("inline image")), } decision, err := generatedAssetDecisionForItem("images.generations", item, generatedAssetUploadPolicyFromName(store.FileStorageResultUploadPolicyUploadNone)) if err != nil { t.Fatalf("unexpected error: %v", err) } if decision.Inline == nil || decision.URL != nil { t.Fatalf("upload_none should still turn inline payloads into static URLs: %+v", decision) } if !containsString(decision.StripKeys, "b64_json") { t.Fatalf("inline payload should be stripped before persistence: %+v", decision.StripKeys) } } func TestGeneratedAssetUploadPolicyFromName(t *testing.T) { tests := []struct { name string policyName string want generatedAssetUploadPolicy }{ { name: "default", policyName: store.FileStorageResultUploadPolicyDefault, want: generatedAssetUploadPolicy{UploadInlineMedia: true, UploadURLMedia: false, StoreInlineMediaLocally: false}, }, { name: "upload all", policyName: store.FileStorageResultUploadPolicyUploadAll, want: generatedAssetUploadPolicy{UploadInlineMedia: true, UploadURLMedia: true, StoreInlineMediaLocally: false}, }, { name: "upload none", policyName: store.FileStorageResultUploadPolicyUploadNone, want: generatedAssetUploadPolicy{UploadInlineMedia: true, UploadURLMedia: false, StoreInlineMediaLocally: true}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := generatedAssetUploadPolicyFromName(tt.policyName) if got != tt.want { t.Fatalf("unexpected policy: got %+v, want %+v", got, tt.want) } }) } } func TestResolvedGeneratedAssetContentTypePrefersDetectedMedia(t *testing.T) { pngPayload := []byte{0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0} contentType := resolvedGeneratedAssetContentType("image/jpeg", "image", pngPayload) if contentType != "image/png" { t.Fatalf("expected detected PNG content type, got %s", contentType) } if extension := fileExtensionForContentType(contentType, "image"); extension != ".png" { t.Fatalf("expected PNG extension, got %s", extension) } } func TestResolvedGeneratedAssetContentTypeKeepsDeclaredMediaWhenDetectionIsGeneric(t *testing.T) { contentType := resolvedGeneratedAssetContentType("image/webp", "image", []byte("not enough media bytes")) if contentType != "image/webp" { t.Fatalf("expected declared webp content type, got %s", contentType) } } func TestGeneratedAssetFileNameIsUniqueAndTyped(t *testing.T) { first := generatedAssetFileName("663e19cd4fa9d8078385c7c9", 0, "image/png", "image") second := generatedAssetFileName("663e19cd4fa9d8078385c7c9", 0, "image/png", "image") if first == second { t.Fatalf("expected generated file names to be unique, both were %s", first) } if !strings.HasPrefix(first, "gateway-result-663e19cd4fa9d8078385c7c9-01-") || !strings.HasSuffix(first, ".png") { t.Fatalf("unexpected generated file name: %s", first) } } func TestUploadGeneratedAssetStoresLocalWhenNoChannels(t *testing.T) { storageDir := t.TempDir() service := &Service{cfg: config.Config{LocalGeneratedStorageDir: storageDir}} payload := []byte{0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a, 0, 0, 0, 0} asset := &generatedInlineAsset{ Bytes: payload, ContentType: "image/jpeg", Kind: "image", SourceKey: "b64_json", } upload, contentType, kind, strategy, err := service.uploadGeneratedAsset(context.Background(), "task-123", asset, 0, nil, false) if err != nil { t.Fatalf("unexpected error: %v", err) } if contentType != "image/png" || kind != "image" || strategy != "local_static_inline_media" { t.Fatalf("unexpected local upload metadata: contentType=%s kind=%s strategy=%s", contentType, kind, strategy) } urlValue := stringFromAny(upload["url"]) if !strings.HasPrefix(urlValue, "/static/generated/gateway-result-task-123-01-") || !strings.HasSuffix(urlValue, ".png") { t.Fatalf("unexpected local static URL: %s", urlValue) } entries, err := os.ReadDir(storageDir) if err != nil { t.Fatalf("failed to read local static dir: %v", err) } if len(entries) != 1 || !strings.HasSuffix(entries[0].Name(), ".png") { t.Fatalf("expected one PNG file in local static dir, got %+v", entries) } stored, err := os.ReadFile(filepath.Join(storageDir, entries[0].Name())) if err != nil { t.Fatalf("failed to read local static file: %v", err) } if !bytes.Equal(stored, payload) { t.Fatalf("stored payload does not match source payload") } } func TestUploadFileStoresLocalWhenNoChannels(t *testing.T) { storageDir := t.TempDir() service := &Service{cfg: config.Config{ LocalUploadedStorageDir: storageDir, ServerMainBaseURL: "http://127.0.0.1:1", ServerMainInternalToken: "change-me", }} payload := []byte("%PDF-1.4") upload, err := service.UploadFile(context.Background(), FileUploadPayload{ Bytes: payload, ContentType: "application/pdf", FileName: "用户文件.png", Source: "playground", }) if err != nil { t.Fatalf("unexpected error: %v", err) } urlValue := stringFromAny(upload["url"]) if !strings.HasPrefix(urlValue, "/static/uploaded/") || !strings.HasSuffix(urlValue, ".pdf") { t.Fatalf("unexpected uploaded local static URL: %s", urlValue) } storageChannel, _ := upload["storageChannel"].(map[string]any) if stringFromAny(storageChannel["provider"]) != "local_static" { t.Fatalf("expected local static provider metadata, got %+v", upload["storageChannel"]) } assetStorage, _ := upload["assetStorage"].(map[string]any) if stringFromAny(assetStorage["strategy"]) != "local_static_upload" || stringFromAny(assetStorage["scene"]) != store.FileStorageSceneUpload { t.Fatalf("unexpected upload asset storage metadata: %+v", assetStorage) } entries, err := os.ReadDir(storageDir) if err != nil { t.Fatalf("failed to read uploaded static dir: %v", err) } if len(entries) != 1 || !strings.HasSuffix(entries[0].Name(), ".pdf") { t.Fatalf("expected one PDF file in uploaded static dir, got %+v", entries) } stored, err := os.ReadFile(filepath.Join(storageDir, entries[0].Name())) if err != nil { t.Fatalf("failed to read uploaded static file: %v", err) } if !bytes.Equal(stored, payload) { t.Fatalf("stored uploaded payload does not match source payload") } }