178 lines
7.6 KiB
Go
178 lines
7.6 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v3"
|
|
|
|
"git.ipao.vip/rogee/creator-hub/internal/creator"
|
|
hub "git.ipao.vip/rogee/creator-hub/internal/environment"
|
|
)
|
|
|
|
func TestDecodeCreatorRejectsUnknownAndTrailingJSON(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Post("/", func(c fiber.Ctx) error {
|
|
var value struct {
|
|
Name string `json:"name"`
|
|
}
|
|
if err := decodeCreator(c, &value); err != nil {
|
|
return fiber.ErrBadRequest
|
|
}
|
|
return c.SendStatus(http.StatusNoContent)
|
|
})
|
|
valid := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{"name":"ok"}`))
|
|
valid.Header.Set("Content-Type", "application/json")
|
|
response, err := app.Test(valid)
|
|
if err != nil || response.StatusCode != http.StatusNoContent {
|
|
t.Fatalf("valid creator JSON status = %d, err = %v", response.StatusCode, err)
|
|
}
|
|
for _, body := range []string{`{"unknown":true}`, `{"name":"ok"}{"name":"extra"}`} {
|
|
request := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
response, err := app.Test(request)
|
|
if err != nil || response.StatusCode != http.StatusBadRequest {
|
|
t.Errorf("invalid creator JSON %q status = %d, err = %v", body, response.StatusCode, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCreatorErrorMapsDomainErrorsAndReasons(t *testing.T) {
|
|
for _, testCase := range []struct {
|
|
err error
|
|
status int
|
|
}{
|
|
{err: creator.ErrInvalid, status: http.StatusBadRequest},
|
|
{err: creator.ErrConflict, status: http.StatusConflict},
|
|
{err: creator.ErrNotFound, status: http.StatusNotFound},
|
|
{err: creator.ErrUnavailable, status: http.StatusServiceUnavailable},
|
|
{err: creator.ErrUncertain, status: http.StatusConflict},
|
|
{err: errors.New("unexpected"), status: http.StatusInternalServerError},
|
|
} {
|
|
app := fiber.New()
|
|
app.Get("/", func(c fiber.Ctx) error { return creatorError(c, testCase.err) })
|
|
response, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil))
|
|
if err != nil || response.StatusCode != testCase.status {
|
|
t.Errorf("creator error %v status = %d, err = %v", testCase.err, response.StatusCode, err)
|
|
}
|
|
}
|
|
app := fiber.New()
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
return creatorError(c, fmt.Errorf("%w: gateway stopped", creator.ErrUnavailable))
|
|
})
|
|
response, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil))
|
|
if err != nil || response.StatusCode != http.StatusServiceUnavailable {
|
|
t.Fatalf("unavailable reason status = %d, err = %v", response.StatusCode, err)
|
|
}
|
|
}
|
|
|
|
func TestCreatorPageQueryRejectsNonNumericValues(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
_, _, _, err := creatorPageQuery(c)
|
|
if err != nil {
|
|
return fiber.ErrBadRequest
|
|
}
|
|
return c.SendStatus(http.StatusNoContent)
|
|
})
|
|
for _, query := range []string{"page=bad", "page_size=bad"} {
|
|
response, err := app.Test(httptest.NewRequest(http.MethodGet, "/?"+query, nil))
|
|
if err != nil || response.StatusCode != http.StatusBadRequest {
|
|
t.Errorf("invalid page query %q status = %d, err = %v", query, response.StatusCode, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWorkFilterParsesNumericAndTimeFilters(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Get("/", func(c fiber.Ctx) error {
|
|
filter, err := workFilter(c)
|
|
if err != nil {
|
|
return fiber.ErrBadRequest
|
|
}
|
|
return c.JSON(filter)
|
|
})
|
|
request := httptest.NewRequest(http.MethodGet, "/?platform=douyin&source_id=source-1&source_type=owned&published_at_status=published&min_likes=1&min_comments=2&min_shares=3&published_after=2026-09-17T00:00:00Z&published_before=2026-09-18T00:00:00Z", nil)
|
|
response, err := app.Test(request)
|
|
if err != nil || response.StatusCode != http.StatusOK {
|
|
t.Fatalf("valid work filter status = %d, err = %v", response.StatusCode, err)
|
|
}
|
|
for _, query := range []string{"min_likes=bad", "min_comments=bad", "min_shares=bad", "published_after=bad", "published_before=bad"} {
|
|
response, err := app.Test(httptest.NewRequest(http.MethodGet, "/?"+query, nil))
|
|
if err != nil || response.StatusCode != http.StatusBadRequest {
|
|
t.Errorf("invalid work filter %q status = %d, err = %v", query, response.StatusCode, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type accountEnvironmentTestStore struct {
|
|
environment hub.EnvironmentContext
|
|
err error
|
|
}
|
|
|
|
func (s accountEnvironmentTestStore) GetEnvironmentContextForAccount(context.Context, string) (hub.EnvironmentContext, error) {
|
|
return s.environment, s.err
|
|
}
|
|
|
|
func TestCreatorSchedulerAndAccountEnvironmentFailWithoutStores(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
runCreatorScheduler(ctx, nil, nil, nil)
|
|
if _, found, err := accountEnvironment(context.Background(), nil, "account-1"); !errors.Is(err, creator.ErrUnavailable) || found {
|
|
t.Fatalf("nil account environment store = found=%v err=%v", found, err)
|
|
}
|
|
if _, found, err := accountEnvironment(context.Background(), accountEnvironmentTestStore{err: hub.ErrNotFound}, "account-1"); err != nil || found {
|
|
t.Fatalf("missing account environment = found=%v err=%v", found, err)
|
|
}
|
|
sentinel := errors.New("database failed")
|
|
if _, found, err := accountEnvironment(context.Background(), accountEnvironmentTestStore{err: sentinel}, "account-1"); !errors.Is(err, sentinel) || found {
|
|
t.Fatalf("account environment error = found=%v err=%v", found, err)
|
|
}
|
|
environment := testRunnableEnvironment()
|
|
got, found, err := accountEnvironment(context.Background(), accountEnvironmentTestStore{environment: environment}, "account-1")
|
|
if err != nil || !found || got.RuntimeID != environment.RuntimeID {
|
|
t.Fatalf("account environment = %#v, found=%v, err=%v", got, found, err)
|
|
}
|
|
}
|
|
|
|
func TestCreatorPreviewAndLifecycleHelpersRejectUnavailableDependencies(t *testing.T) {
|
|
if _, err := previewCompetitorShare(context.Background(), nil, nil, nil, "account-1", "", "not-a-url"); err == nil {
|
|
t.Fatal("invalid competitor share URL was accepted")
|
|
}
|
|
if _, err := previewCompetitorShare(context.Background(), nil, nil, nil, "account-1", creator.PlatformXiaohongshu, "https://www.douyin.com/video/123"); err == nil {
|
|
t.Fatal("platform mismatch was accepted")
|
|
}
|
|
if _, err := previewCompetitorShare(context.Background(), nil, nil, nil, "account-1", "", "https://www.douyin.com/video/123"); err == nil {
|
|
t.Fatal("unavailable Douyin preview was reported as successful")
|
|
}
|
|
if _, err := previewCompetitorShare(context.Background(), nil, nil, nil, "account-1", "", "https://www.xiaohongshu.com/explore/123"); err == nil {
|
|
t.Fatal("unavailable Xiaohongshu preview was reported as successful")
|
|
}
|
|
if got := (competitorSharePreview{Platform: creator.PlatformDouyin, PlatformAccountKey: "uid", Nickname: "name", AvatarURL: "avatar", HomepageURL: "home"}).input(); got.PlatformAccountKey != "uid" || got.Nickname != "name" {
|
|
t.Fatalf("preview input = %#v", got)
|
|
}
|
|
if _, err := newDouyinAccountBrowser(context.Background(), nil, nil, nil, "account-1"); err == nil {
|
|
t.Fatal("missing Douyin dependencies were accepted")
|
|
}
|
|
if _, err := verifyCreatorAccount(context.Background(), nil, nil, nil, "account-1"); err == nil {
|
|
t.Fatal("missing account verification dependencies were accepted")
|
|
}
|
|
if _, err := creatorLoginQRCode(context.Background(), nil, nil, nil, "account-1"); err == nil {
|
|
t.Fatal("missing login QR dependencies were accepted")
|
|
}
|
|
if _, err := processCreatorMaterial(context.Background(), nil, nil, nil, "work-1"); err == nil {
|
|
t.Fatal("missing material dependencies were accepted")
|
|
}
|
|
if err := startCreatorEnvironment(context.Background(), nil, hub.EnvironmentContext{}); err == nil {
|
|
t.Fatal("missing runtime store was accepted")
|
|
}
|
|
if _, err := creatorCollectionAccount(context.Background(), nil, nil, nil, creator.PlatformDouyin); err == nil {
|
|
t.Fatal("missing collection dependencies were accepted")
|
|
}
|
|
}
|