118 lines
6.0 KiB
Go
118 lines
6.0 KiB
Go
package creator
|
|
|
|
import (
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestBuildCommentAggregationQuery(t *testing.T) {
|
|
now := time.Date(2026, 9, 20, 12, 0, 0, 0, time.FixedZone("local", 8*3600))
|
|
for _, source := range []string{SourceOwned, SourceCompetitor} {
|
|
for _, hours := range []int{1, 6, 12, 24, 72, 120, 168} {
|
|
query, args, err := buildCommentAggregationQuery(CommentAggregationFilter{SourceType: source, SourceID: "account-1", Hours: hours}, now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, clause := range []string{"w.source_type = $1", "c.published_at >= $2", "c.published_at <= $3", "w.source_id = $4", "JOIN creator_work w ON w.id = c.work_id"} {
|
|
if !strings.Contains(query, clause) {
|
|
t.Fatalf("missing %q in %s", clause, query)
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(args, []any{source, now.UTC().Add(-time.Duration(hours) * time.Hour), now.UTC(), "account-1"}) {
|
|
t.Fatalf("args = %#v", args)
|
|
}
|
|
}
|
|
}
|
|
query, args, err := buildCommentAggregationQuery(CommentAggregationFilter{SourceType: SourceOwned, Hours: 24}, now)
|
|
if err != nil || len(args) != 3 || strings.Contains(query, "w.source_id =") {
|
|
t.Fatalf("all accounts query=%s args=%v err=%v", query, args, err)
|
|
}
|
|
for _, filter := range []CommentAggregationFilter{{Hours: 24}, {SourceType: "other", Hours: 24}, {SourceType: SourceOwned}, {SourceType: SourceOwned, Hours: -1}, {SourceType: SourceOwned, Hours: 2}, {SourceType: SourceOwned, Hours: 169}} {
|
|
if _, _, err := buildCommentAggregationQuery(filter, now); !errors.Is(err, ErrInvalid) {
|
|
t.Fatalf("filter=%+v err=%v", filter, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCommentAggregationPageIntegration(t *testing.T) {
|
|
store, accounts, ctx := openCreatorIntegrationStore(t)
|
|
now := time.Now().UTC().Truncate(time.Second)
|
|
ownedID := createIntegrationAccount(t, ctx, accounts, "comments-owned")
|
|
competitor, err := store.UpsertCompetitor(ctx, CompetitorInput{Platform: PlatformDouyin, PlatformAccountKey: "comments-competitor", HomepageURL: "https://www.douyin.com/user/comments-competitor", Nickname: "竞品昵称"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addWork := func(key, source, id string) Work {
|
|
work, _, err := store.UpsertWork(ctx, WorkInput{Platform: PlatformDouyin, WorkKey: key, SourceType: source, SourceID: id, Title: key, OriginalURL: "https://www.douyin.com/video/" + key}, now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return work
|
|
}
|
|
owned := addWork("owned-comments", SourceOwned, ownedID)
|
|
rival := addWork("competitor-comments", SourceCompetitor, competitor.ID)
|
|
addComment := func(work Work, key string, at *time.Time) Comment {
|
|
comment, _, err := store.SaveComment(ctx, CommentInput{Platform: PlatformDouyin, WorkID: work.ID, CommentKey: key, AuthorUID: "reader-uid", AuthorName: "评论者", Content: key, PublishedAt: at, CommentType: "top_level"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return comment
|
|
}
|
|
newest := addComment(owned, "newest", &now)
|
|
boundary := now.Add(-time.Hour)
|
|
addComment(owned, "boundary", &boundary)
|
|
addComment(owned, "same-time", &boundary)
|
|
addComment(owned, "unknown", nil)
|
|
future := now.Add(time.Second)
|
|
addComment(owned, "future", &future)
|
|
for _, hours := range []int{6, 12, 24, 72, 120, 168} {
|
|
at := now.Add(-time.Duration(hours) * time.Hour)
|
|
addComment(owned, "age-"+at.Format(time.RFC3339), &at)
|
|
}
|
|
old := now.Add(-168*time.Hour - time.Second)
|
|
addComment(owned, "old", &old)
|
|
rivalComment := addComment(rival, "rival", &now)
|
|
for index, hours := range []int{1, 6, 12, 24, 72, 120, 168} {
|
|
page, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceOwned, Hours: hours}, 1, 100, now)
|
|
if err != nil || page.Total != 3+index || len(page.Data) != 3+index {
|
|
t.Fatalf("hours=%d page=%+v err=%v", hours, page, err)
|
|
}
|
|
if page.Data[0].ID != newest.ID || page.Data[0].SourceID != ownedID || page.Data[0].SourceType != SourceOwned || page.Data[0].SourceName == "" || page.Data[0].SourceAccountKey == "" || page.Data[0].WorkTitle != owned.Title || page.Data[0].WorkURL != owned.OriginalURL || page.Data[0].AuthorUID != "reader-uid" {
|
|
t.Fatalf("metadata=%+v", page.Data[0])
|
|
}
|
|
}
|
|
first, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceOwned, SourceID: ownedID, Hours: 1}, 1, 2, now)
|
|
if err != nil || first.Total != 3 || len(first.Data) != 2 || !first.HasNext {
|
|
t.Fatalf("first=%+v err=%v", first, err)
|
|
}
|
|
second, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceOwned, Hours: 1}, 2, 2, now)
|
|
if err != nil || second.Total != 3 || len(second.Data) != 1 || second.HasNext || second.Data[0].ID == first.Data[1].ID {
|
|
t.Fatalf("second=%+v err=%v", second, err)
|
|
}
|
|
for _, filter := range []CommentAggregationFilter{{SourceType: SourceOwned, SourceID: competitor.ID, Hours: 1}, {SourceType: SourceCompetitor, SourceID: ownedID, Hours: 1}} {
|
|
page, err := store.ListCommentAggregationPage(ctx, filter, 1, 25, now)
|
|
if err != nil || page.Total != 0 || page.Data == nil || len(page.Data) != 0 {
|
|
t.Fatalf("cross source=%+v err=%v", page, err)
|
|
}
|
|
}
|
|
page, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceCompetitor, Hours: 1}, 1, 25, now)
|
|
if err != nil || page.Total != 1 || page.Data[0].ID != rivalComment.ID || page.Data[0].SourceName != "竞品昵称" || page.Data[0].SourceAccountKey != "comments-competitor" {
|
|
t.Fatalf("competitor=%+v err=%v", page, err)
|
|
}
|
|
page, err = store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceOwned, Hours: 1}, 10, 25, now)
|
|
if err != nil || page.Total != 3 || page.Data == nil || len(page.Data) != 0 {
|
|
t.Fatalf("out of range=%+v err=%v", page, err)
|
|
}
|
|
for _, pagination := range [][2]int{{0, 25}, {1, 0}, {1, 101}} {
|
|
if _, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: SourceOwned, Hours: 1}, pagination[0], pagination[1], now); !errors.Is(err, ErrInvalid) {
|
|
t.Fatalf("pagination=%v err=%v", pagination, err)
|
|
}
|
|
}
|
|
if _, err := store.ListCommentAggregationPage(ctx, CommentAggregationFilter{SourceType: "invalid", Hours: 1}, 1, 25, now); !errors.Is(err, ErrInvalid) {
|
|
t.Fatal(err)
|
|
}
|
|
}
|