package creator import ( "errors" "strings" "testing" "time" ) func TestBuildWorkQueryAnalysis(t *testing.T) { for _, field := range []string{"published_at", "likes", "comments_count", "shares", "collect_count", "play_count", "likes_growth"} { for _, order := range []string{"asc", "desc"} { query, _, err := buildWorkQuery(WorkFilter{SortBy: field, SortOrder: order}) if err != nil { t.Fatalf("%s %s: %v", field, order, err) } expected := field + " " + strings.ToUpper(order) + " NULLS LAST" if field == "likes_growth" { expected = "published_at DESC NULLS LAST" } if !strings.Contains(query, expected) || !strings.HasSuffix(query, ", id") { t.Fatalf("unexpected query: %s", query) } } } after := time.Now().Add(-time.Hour) before := time.Now() query, args, err := buildWorkQuery(WorkFilter{Platform: "douyin", SourceType: SourceOwned, SourceID: "owned-1", PublishedAtStatus: "verified", PublishedAfter: &after, PublishedBefore: &before, MinLikes: int64Ptr(0), MinComments: int64Ptr(1), MinShares: int64Ptr(2), MinCollectCount: int64Ptr(3), MinPlayCount: int64Ptr(4)}) if err != nil || len(args) != 11 { t.Fatalf("query=%s args=%v err=%v", query, args, err) } for _, column := range []string{"likes", "comments_count", "shares", "collect_count", "play_count"} { if !strings.Contains(query, column+" IS NOT NULL AND "+column+" >=") { t.Fatalf("nil threshold must be excluded: %s", query) } } query, _, err = buildWorkQuery(WorkFilter{}) if err != nil || !strings.Contains(query, "ORDER BY published_at DESC NULLS LAST, id") { t.Fatalf("default: %s %v", query, err) } for _, status := range []string{"verified", "future", "pending_verification", "invalid"} { if _, _, err := buildWorkQuery(WorkFilter{PublishedAtStatus: status, SourceType: SourceCompetitor}); err != nil { t.Fatal(err) } } for _, filter := range []WorkFilter{ {SortBy: "likes; DROP TABLE creator_work"}, {SortOrder: "invalid"}, {Platform: "invalid"}, {SourceType: "invalid"}, {PublishedAtStatus: "unknown"}, {PublishedAfter: &before, PublishedBefore: &after}, {MinLikes: int64Ptr(-1)}, {MinComments: int64Ptr(-1)}, {MinShares: int64Ptr(-1)}, {MinCollectCount: int64Ptr(-1)}, {MinPlayCount: int64Ptr(-1)}, } { if _, _, err := buildWorkQuery(filter); !errors.Is(err, ErrInvalid) { t.Fatalf("invalid filter accepted: %+v, %v", filter, err) } } } func TestAnalysisGrowthSortBeforePagination(t *testing.T) { works := []Work{{ID: "unknown"}, {ID: "low", LikesGrowth: int64Ptr(1)}, {ID: "high", LikesGrowth: int64Ptr(9)}, {ID: "equal", LikesGrowth: int64Ptr(9)}} for _, tc := range []struct { order string ids []string }{ {"desc", []string{"high", "equal", "low", "unknown"}}, {"asc", []string{"low", "high", "equal", "unknown"}}, } { items := append([]Work(nil), works...) sortWorksGrowth(items, tc.order) for i, id := range tc.ids { if items[i].ID != id { t.Fatalf("%s: %+v", tc.order, items) } } page, err := slicePage(items, 2, 2) if err != nil || page.Total != 4 || page.Data[0].ID != tc.ids[2] { t.Fatalf("page=%+v err=%v", page, err) } } sortWorksGrowth([]Work{{ID: "a"}, {ID: "b"}}, "desc") filtered, err := ApplyWorkGrowthFilter(works, nil, WorkGrowthFilter{MinLikes: int64Ptr(0)}) if err != nil || len(filtered) != 3 || filtered[0].ID != "low" { t.Fatalf("filter must preserve explicit sorting: %+v %v", filtered, err) } }