package api import ( "bytes" "fmt" "io" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "sync" "testing" "time" "git.ipao.vip/rogee/creator-hub/internal/account" "git.ipao.vip/rogee/creator-hub/internal/creator" hub "git.ipao.vip/rogee/creator-hub/internal/environment" "github.com/gofiber/fiber/v3" ) func TestOwnedCoversCacheFilesAndReportIndividualFailures(t *testing.T) { databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL") if databaseURL == "" { t.Skip("set CREATORHUB_POSTGRES_TEST_URL for isolated PostgreSQL coverage") } store, accounts, ctx := openCreatorIntegrationStoreForAPITest(t, databaseURL) owner := account.Account{ ID: "owned-cover-account", Name: "封面账号", Platform: creator.PlatformDouyin, PlatformAccountKey: "12345678901", CredentialReference: account.CredentialReference{ID: "cover-test-credential", Provider: "os_keyring"}, CredentialKey: "creatorhub/owned-cover-account/cookies", } if err := accounts.CreateAccount(ctx, owner, &testCredentialBridge{values: make(map[string]string)}); err != nil { t.Fatal(err) } works := make(map[string]creator.Work) for _, key := range []string{"good", "broken"} { work, _, err := store.UpsertWork(ctx, creator.WorkInput{SourceType: creator.SourceOwned, SourceID: owner.ID, Platform: creator.PlatformDouyin, WorkKey: "owned-cover-" + key, CoverURL: "https://p3-pc-sign.douyinpic.com/" + key + ".png"}, time.Now().UTC()) if err != nil { t.Fatal(err) } works[key] = work } requests := 0 var mu sync.Mutex cleanup := startFakeChannelGateway(t, 120, "owned-cover-key-00120", map[string]string{}, func(task taskFrame) { mu.Lock() requests++ mu.Unlock() }) defer cleanup() _ = io.Discard _ = strings.Contains browser := creatorGatewayBrowser{gateway: hub.Gateway{ID: 120, Name: "gw", AccessKey: "owned-cover-key-00120"}, environment: hub.EnvironmentContext{Env: hub.Env{Alias: "owned-cover-alias"}, RuntimeID: "runtime-cover", RuntimeNetworkID: "network-cover", BindingVersion: 1}} if err := cacheWorkCovers(ctx, store, browser, creator.SourceOwned, owner.ID); err == nil || !strings.Contains(err.Error(), works["broken"].ID) { t.Fatalf("individual failure was hidden: %v", err) } if requests != 2 { t.Fatalf("failure stopped other covers from downloading: %d", requests) } path, err := store.GetWorkCover(ctx, works["good"].ID) if err != nil { t.Fatal(err) } if filepath.Base(filepath.Dir(path)) != owner.PlatformAccountKey || filepath.Base(path) != "owned-cover-good.png" { t.Fatalf("wrong UID/work-ID layout: %s", path) } missing, err := store.ListWorksMissingCover(ctx, creator.SourceOwned, owner.ID) if err != nil || len(missing) != 1 || missing[0].ID != works["broken"].ID { t.Fatalf("cache retry targets: %v %v", missing, err) } app := fiber.New() RegisterCreator(app, store, accounts, nil) response, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/creator/works/%s/cover", works["good"].ID), nil)) if err != nil { t.Fatal(err) } defer response.Body.Close() body, err := io.ReadAll(response.Body) if err != nil || response.StatusCode != 200 || !strings.HasPrefix(response.Header.Get("Content-Type"), "image/png") || !bytes.Equal(body, []byte("owned-cover-bytes")) { t.Fatalf("static cover response: %d %s %q %v", response.StatusCode, response.Header.Get("Content-Type"), body, err) } }