package runner import ( "bytes" "context" "crypto/hmac" "crypto/sha1" "encoding/base64" "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/config" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestDirectOSSUploadSignsRequestAndReturnsPrivateSignedURL(t *testing.T) { payload := []byte("production-isomorphic-image") var calls atomic.Int64 var uploadedPath string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { call := calls.Add(1) if r.Method != http.MethodPut { t.Errorf("method=%s", r.Method) } if r.Header.Get("Content-Type") != "image/png" || r.Header.Get("Date") == "" { t.Errorf("missing OSS upload headers") } stringToSign := "PUT\n\nimage/png\n" + r.Header.Get("Date") + "\n/media-bucket" + r.URL.EscapedPath() mac := hmac.New(sha1.New, []byte("access-secret")) _, _ = mac.Write([]byte(stringToSign)) expectedAuthorization := "OSS access-key:" + base64.StdEncoding.EncodeToString(mac.Sum(nil)) if r.Header.Get("Authorization") != expectedAuthorization { t.Errorf("invalid OSS request signature") } body, err := io.ReadAll(r.Body) if err != nil || !bytes.Equal(body, payload) { t.Errorf("uploaded payload differs: err=%v", err) } uploadedPath = r.URL.EscapedPath() if call == 1 { http.Error(w, "retry", http.StatusBadGateway) return } w.WriteHeader(http.StatusOK) })) defer server.Close() cfg := config.Config{ MediaOSSDirectEnabled: true, MediaOSSEndpoint: server.URL, MediaOSSBucket: "media-bucket", MediaOSSAccessKeyID: "access-key", MediaOSSAccessKeySecret: "access-secret", MediaOSSPublicBaseURL: "https://cdn.example.com", MediaOSSObjectPrefix: "easyai-ai-gateway/production/media", } uploader := newDirectOSSUploader(cfg) service := &Service{cfg: cfg, directOSS: uploader} uploaded, err := service.UploadFile(context.Background(), FileUploadPayload{ Bytes: payload, ContentType: "image/png", FileName: "input.png", Scene: store.FileStorageSceneRequestAsset, Source: "acceptance", }) if err != nil { t.Fatalf("direct OSS upload: %v", err) } if calls.Load() != 2 { t.Fatalf("upload calls=%d, want one retry", calls.Load()) } if !strings.Contains(uploadedPath, "/easyai-ai-gateway/production/media/request_asset/") || !strings.HasSuffix(uploadedPath, "/"+sha256Hex(payload)+".png") { t.Fatalf("unexpected object path=%q", uploadedPath) } if got := stringFromAny(uploaded["url"]); !strings.HasPrefix(got, server.URL+uploadedPath+"?") || !strings.Contains(got, "Signature=") { t.Fatalf("private signed URL=%q", got) } if uploaded["accessScope"] != "private" { t.Fatalf("request asset access scope=%v", uploaded["accessScope"]) } channel, _ := uploaded["storageChannel"].(map[string]any) if stringFromAny(channel["provider"]) != "aliyun_oss" { t.Fatalf("storage channel=%+v", channel) } } func TestDirectOSSPersistsGeneralUploadScene(t *testing.T) { var calls atomic.Int64 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { calls.Add(1) w.WriteHeader(http.StatusOK) })) defer server.Close() cfg := config.Config{ MediaOSSDirectEnabled: true, MediaOSSEndpoint: server.URL, MediaOSSBucket: "media-bucket", MediaOSSAccessKeyID: "access-key", MediaOSSAccessKeySecret: "access-secret", MediaOSSPublicBaseURL: "https://cdn.example.com", MediaOSSObjectPrefix: "easyai-ai-gateway/production/media", } service := &Service{cfg: cfg, directOSS: newDirectOSSUploader(cfg)} uploaded, err := service.UploadFile(context.Background(), FileUploadPayload{ Bytes: []byte("general-upload"), ContentType: "text/plain", FileName: "general.txt", Scene: store.FileStorageSceneUpload, }) if err != nil { t.Fatalf("general upload: %v", err) } if calls.Load() != 1 { t.Fatalf("direct OSS calls=%d for general upload scene", calls.Load()) } channel, _ := uploaded["storageChannel"].(map[string]any) if stringFromAny(channel["provider"]) != "aliyun_oss" { t.Fatalf("general upload channel=%+v", channel) } } func TestGeneratedInlineAssetUsesDirectOSS(t *testing.T) { var calls atomic.Int64 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { calls.Add(1) if request.Method != http.MethodPut { t.Errorf("method=%s, want PUT", request.Method) } w.WriteHeader(http.StatusOK) })) defer server.Close() cfg := config.Config{ MediaOSSDirectEnabled: true, MediaOSSEndpoint: server.URL, MediaOSSBucket: "media-bucket", MediaOSSAccessKeyID: "access-key", MediaOSSAccessKeySecret: "access-secret", MediaOSSPublicBaseURL: "https://cdn.example.com", MediaOSSObjectPrefix: "easyai-ai-gateway/production/media", } service := &Service{cfg: cfg, directOSS: newDirectOSSUploader(cfg)} upload, contentType, kind, strategy, err := service.uploadGeneratedAsset( context.Background(), "task-direct-oss", &generatedInlineAsset{ Bytes: []byte{0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a}, ContentType: "image/png", Kind: "image", SourceKey: "b64_json", }, 0, []store.FileStorageChannel{service.directOSS.fileStorageChannel()}, ) if err != nil { t.Fatalf("direct generated upload: %v", err) } if calls.Load() != 1 { t.Fatalf("direct OSS calls=%d, want 1", calls.Load()) } if contentType != "image/png" || kind != "image" || strategy != "upload_inline_media" { t.Fatalf("generated metadata=%s/%s/%s", contentType, kind, strategy) } channel, _ := upload["storageChannel"].(map[string]any) if stringFromAny(channel["provider"]) != "aliyun_oss" { t.Fatalf("storage channel=%+v", channel) } }