fix: complete Douyin release remediation
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
package creator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestCreatorPostgresPageAndConversationState(t *testing.T) {
|
||||
store, phaseAStore, ctx := openCreatorIntegrationStore(t)
|
||||
if err := store.Ping(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
store.SetSecretBridge(nil)
|
||||
if err := store.EnsureSchema(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
accountID := createIntegrationAccount(t, ctx, phaseAStore, "pages")
|
||||
if err := store.EnsureAccountProfile(ctx, accountID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
now := time.Now().UTC().Add(-time.Hour)
|
||||
work, inserted, err := store.UpsertWork(ctx, WorkInput{
|
||||
Platform: PlatformDouyin, WorkKey: "page-work", SourceType: SourceOwned, SourceID: accountID,
|
||||
AuthorName: "author", Title: "title", Body: "body", PublishedAt: &now, PublishedAtStatus: "verified",
|
||||
}, now)
|
||||
if err != nil || !inserted {
|
||||
t.Fatalf("upsert work: inserted=%v err=%v", inserted, err)
|
||||
}
|
||||
comment, inserted, err := store.SaveComment(ctx, CommentInput{
|
||||
Platform: PlatformDouyin, CommentKey: "page-comment", WorkID: work.ID,
|
||||
AuthorUID: "peer", AuthorName: "Peer", Content: "hello", CommentType: "top_level",
|
||||
PublishedAt: &now,
|
||||
})
|
||||
if err != nil || !inserted {
|
||||
t.Fatalf("save comment: inserted=%v err=%v", inserted, err)
|
||||
}
|
||||
workPage, err := store.ListWorksPage(ctx, WorkFilter{Platform: PlatformDouyin, SourceID: accountID, SourceType: SourceOwned}, 1, 1)
|
||||
if err != nil || len(workPage.Data) != 1 || workPage.Total != 1 {
|
||||
t.Fatalf("list works page: page=%+v err=%v", workPage, err)
|
||||
}
|
||||
commentPage, err := store.ListCommentsPage(ctx, PlatformDouyin, work.ID, 1, 1)
|
||||
if err != nil || len(commentPage.Data) != 1 || commentPage.Total != 1 {
|
||||
t.Fatalf("list comments page: page=%+v err=%v", commentPage, err)
|
||||
}
|
||||
eventResult, err := store.RecordEvent(ctx, InteractionEvent{
|
||||
Platform: PlatformDouyin, ReceivingAccountID: accountID, EventKey: "page-event",
|
||||
EventType: "dm", InteractorUID: "peer", MessageType: MessageTypeText, MessageText: "incoming",
|
||||
ReceivedAt: now,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.ListEventsPage(ctx, accountID, 1, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.SetEventDisplayed(ctx, eventResult.Event.ID, time.Time{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
messageAt := now.Add(time.Minute)
|
||||
message, inserted, err := store.SaveMessage(ctx, MessageInput{
|
||||
Platform: PlatformDouyin, AccountID: accountID, PeerUID: "peer", PeerName: "Peer",
|
||||
PlatformMessageKey: "page-message", Direction: "inbound", MessageType: MessageTypeText,
|
||||
Text: "incoming", MessageAt: &messageAt,
|
||||
})
|
||||
if err != nil || !inserted {
|
||||
t.Fatalf("save message: inserted=%v err=%v", inserted, err)
|
||||
}
|
||||
if _, err := store.ListMessagesPage(ctx, message.ConversationID, 1, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.UpdateConversationHistoryCursor(ctx, message.ConversationID, "cursor-1", true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.LinkMessageOperation(ctx, message.ID, "missing-platform-message"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if traces, err := store.ListStrategyTraces(ctx, eventResult.Event.ID); err != nil || traces == nil {
|
||||
t.Fatalf("list strategy traces: traces=%v err=%v", traces, err)
|
||||
}
|
||||
if _, _, err := store.SaveComment(ctx, CommentInput{Platform: PlatformDouyin, CommentKey: "page-comment", WorkID: work.ID, Content: "updated", CommentType: "top_level"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := store.NextCollectionWindow(ctx, SourceOwned, accountID, now, time.Hour, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.MarkCollectionBlocked(ctx, SourceOwned, accountID, "coverage block", now, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := store.NextCollectionWindow(ctx, SourceOwned, accountID, now, time.Hour, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.ListListenerBoundaries(ctx, accountID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.ListListenerStates(ctx, accountID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if works, err := store.ListDueMetricWorks(ctx, now); err != nil || works == nil {
|
||||
t.Fatalf("list due metric works: works=%v err=%v", works, err)
|
||||
}
|
||||
settings, err := store.GetSettings(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.EnsureMetricPlan(ctx, work.ID, settings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.StopMetricPlan(ctx, work.ID, "coverage stop"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lease, err := store.ClaimSourceSync(ctx, SourceOwned, accountID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.ClaimSourceSync(ctx, SourceOwned, accountID); !errors.Is(err, ErrConflict) {
|
||||
t.Fatalf("claim active source lease: %v", err)
|
||||
}
|
||||
if err := store.ReleaseSourceSync(ctx, SourceOwned, accountID, "wrong-token"); !errors.Is(err, ErrConflict) {
|
||||
t.Fatalf("release wrong lease token: %v", err)
|
||||
}
|
||||
if err := store.ReleaseSourceSync(ctx, SourceOwned, accountID, lease); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.RecordLoginResult(ctx, accountID, "needs_login", "coverage", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.RecordVerifiedLoginResult(ctx, accountID, "sec_uid_"+accountID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := store.GenerateRewrite(ctx, work.ID, nil); !errors.Is(err, ErrUnavailable) {
|
||||
t.Fatalf("generate rewrite without generator: %v", err)
|
||||
}
|
||||
if _, err := store.ConfirmRewrite(ctx, work.ID, "coverage rewrite"); !errors.Is(err, ErrConflict) {
|
||||
t.Fatalf("confirm incomplete rewrite: %v", err)
|
||||
}
|
||||
rule, err := store.CreateRule(ctx, LeadRuleInput{Name: "coverage rule", Enabled: true, SourceType: SourceOwned, Topic: "title", IncludeKeywords: []string{"hello"}, AIRequirement: "business lead"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
analysis, err := store.AnalyzeComments(ctx, []string{comment.ID, comment.ID}, rule.ID, nil)
|
||||
if len(analysis) != 1 || !errors.Is(err, ErrUnavailable) {
|
||||
t.Fatalf("analyze comments without AI: analysis=%v err=%v", analysis, err)
|
||||
}
|
||||
strategyInput := StrategyInput{Enabled: true, Action: ActionReplyComment, EventTypes: []string{"comment"}}
|
||||
if err := store.validateEnabledStrategy(ctx, accountID, strategyInput); !errors.Is(err, ErrInvalid) {
|
||||
t.Fatalf("strategy without reply requirements: %v", err)
|
||||
}
|
||||
if _, err := store.UpdateAccountProfile(ctx, accountID, AccountProfileUpdate{RealNameStatus: "unknown", BusinessStatus: "normal", ReplyRequirements: "answer", CooldownSeconds: 60}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := store.validateEnabledStrategy(ctx, accountID, strategyInput); !errors.Is(err, ErrUnavailable) {
|
||||
t.Fatalf("strategy without AI: %v", err)
|
||||
}
|
||||
missing := []struct {
|
||||
name string
|
||||
get func() error
|
||||
}{
|
||||
{"comment", func() error { _, err := store.GetComment(ctx, "missing"); return err }},
|
||||
{"comment by key", func() error { _, err := store.GetCommentByKey(ctx, PlatformDouyin, "missing"); return err }},
|
||||
{"competitor", func() error { _, err := store.GetCompetitor(ctx, "missing"); return err }},
|
||||
{"conversation", func() error { _, err := store.GetConversation(ctx, "missing"); return err }},
|
||||
{"event", func() error { _, err := store.GetEvent(ctx, "missing"); return err }},
|
||||
{"listener", func() error { _, err := store.GetListenerState(ctx, accountID); return err }},
|
||||
{"message", func() error { _, err := store.GetMessage(ctx, "missing"); return err }},
|
||||
{"operation", func() error { _, err := store.GetOperation(ctx, "missing"); return err }},
|
||||
{"rule", func() error { _, err := store.GetRule(ctx, "missing"); return err }},
|
||||
{"strategy", func() error { _, err := store.GetStrategy(ctx, "missing"); return err }},
|
||||
{"work", func() error { _, err := store.GetWork(ctx, "missing"); return err }},
|
||||
{"work by key", func() error { _, err := store.GetWorkByKey(ctx, PlatformDouyin, "missing"); return err }},
|
||||
}
|
||||
for _, item := range missing {
|
||||
if err := item.get(); !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("get missing %s: %v", item.name, err)
|
||||
}
|
||||
}
|
||||
for _, item := range []struct {
|
||||
name string
|
||||
set func() error
|
||||
}{
|
||||
{"competitor", func() error { _, err := store.SetCompetitorEnabled(ctx, "missing", true); return err }},
|
||||
{"event", func() error { _, err := store.SetEventDisplayed(ctx, "missing", now); return err }},
|
||||
{"rule", func() error { _, err := store.SetRuleEnabled(ctx, "missing", true); return err }},
|
||||
{"strategy", func() error { _, err := store.SetStrategyEnabled(ctx, "missing", true); return err }},
|
||||
} {
|
||||
if err := item.set(); !errors.Is(err, ErrNotFound) {
|
||||
t.Fatalf("set missing %s: %v", item.name, err)
|
||||
}
|
||||
}
|
||||
if err := store.DeleteStrategy(ctx, "missing"); err != nil {
|
||||
t.Fatalf("delete missing strategy: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user