fix: sync harden channel reads and suggested approvals

This commit is contained in:
iamxvbaba 2026-07-28 16:22:33 +08:00
parent 037ce017d4
commit 209a0d279b
14 changed files with 467 additions and 26 deletions

View file

@ -133,3 +133,122 @@ func TestSuggestedPostLifecyclePostgres(t *testing.T) {
t.Fatalf("late settlement balance/channel=%d/%d, want 90/8", debit, channelBalance)
}
}
func TestSuggestedPostApprovalAcceptsDelayedSchedulePostgres(t *testing.T) {
pool := testPool(t)
ctx := context.Background()
suffix := randomSuffix(t)
users := NewUserStore(pool)
owner, err := users.Create(ctx, domain.User{AccessHash: 211, Phone: "+1889" + suffix + "01", FirstName: "DelayedOwner"})
if err != nil {
t.Fatal(err)
}
subscriber, err := users.Create(ctx, domain.User{AccessHash: 212, Phone: "+1889" + suffix + "02", FirstName: "DelayedSubscriber"})
if err != nil {
t.Fatal(err)
}
channels := NewChannelStore(pool)
created, err := channels.CreateChannel(ctx, domain.CreateChannelRequest{
CreatorUserID: owner.ID, Title: "Delayed Suggested " + suffix, Broadcast: true, Date: 1_700_020_000,
})
if err != nil {
t.Fatal(err)
}
enabled, err := channels.SetPaidMessagesPrice(ctx, owner.ID, created.Channel.ID, 0, true)
if err != nil {
t.Fatal(err)
}
monoID := enabled.Channel.LinkedMonoforumID
t.Cleanup(func() {
_, _ = pool.Exec(ctx, `DELETE FROM suggested_post_approvals WHERE monoforum_id=$1`, monoID)
_, _ = pool.Exec(ctx, `DELETE FROM channels WHERE id=ANY($1::bigint[])`, []int64{monoID, created.Channel.ID})
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id=ANY($1::bigint[])`, []int64{owner.ID, subscriber.ID})
})
saved := domain.Peer{Type: domain.PeerTypeUser, ID: subscriber.ID}
const now = 1_700_020_100
near, err := channels.SendMonoforumMessage(ctx, domain.SendMonoforumMessageRequest{
MonoforumID: monoID, SenderUserID: subscriber.ID, SavedPeer: saved,
RandomID: 81, Message: "postgres near schedule", SuggestedPost: &domain.SuggestedPost{}, Date: now - 10,
})
if err != nil {
t.Fatal(err)
}
nearDate := now + 2*60
accepted, err := channels.ToggleSuggestedPostApproval(ctx, domain.ToggleSuggestedPostApprovalRequest{
UserID: owner.ID, MonoforumID: monoID, MessageID: near.Message.ID,
ScheduleDate: nearDate, Date: now,
})
if err != nil || accepted.State != domain.SuggestedPostStateScheduled || accepted.Published != nil {
t.Fatalf("near schedule approval=%+v err=%v", accepted, err)
}
var monoPts, parentPts, monoEvents, parentEvents int
if err := pool.QueryRow(ctx, `SELECT pts FROM channels WHERE id=$1`, monoID).Scan(&monoPts); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT pts FROM channels WHERE id=$1`, created.Channel.ID).Scan(&parentPts); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT count(*)::int FROM channel_update_events WHERE channel_id=$1`, monoID).Scan(&monoEvents); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT count(*)::int FROM channel_update_events WHERE channel_id=$1`, created.Channel.ID).Scan(&parentEvents); err != nil {
t.Fatal(err)
}
replay, err := channels.ToggleSuggestedPostApproval(ctx, domain.ToggleSuggestedPostApprovalRequest{
UserID: owner.ID, MonoforumID: monoID, MessageID: near.Message.ID,
ScheduleDate: nearDate, Date: now + 10*60,
})
if err != nil || !replay.Duplicate {
t.Fatalf("late replay=%+v err=%v", replay, err)
}
var gotMonoPts, gotParentPts, gotMonoEvents, gotParentEvents int
if err := pool.QueryRow(ctx, `SELECT pts FROM channels WHERE id=$1`, monoID).Scan(&gotMonoPts); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT pts FROM channels WHERE id=$1`, created.Channel.ID).Scan(&gotParentPts); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT count(*)::int FROM channel_update_events WHERE channel_id=$1`, monoID).Scan(&gotMonoEvents); err != nil {
t.Fatal(err)
}
if err := pool.QueryRow(ctx, `SELECT count(*)::int FROM channel_update_events WHERE channel_id=$1`, created.Channel.ID).Scan(&gotParentEvents); err != nil {
t.Fatal(err)
}
if gotMonoPts != monoPts || gotParentPts != parentPts || gotMonoEvents != monoEvents || gotParentEvents != parentEvents {
t.Fatalf("late replay changed pts/events mono=%d/%d→%d/%d parent=%d/%d→%d/%d",
monoPts, monoEvents, gotMonoPts, gotMonoEvents, parentPts, parentEvents, gotParentPts, gotParentEvents)
}
due, err := channels.SendMonoforumMessage(ctx, domain.SendMonoforumMessageRequest{
MonoforumID: monoID, SenderUserID: subscriber.ID, SavedPeer: saved,
RandomID: 82, Message: "postgres due schedule", SuggestedPost: &domain.SuggestedPost{}, Date: now + 20,
})
if err != nil {
t.Fatal(err)
}
approvedAt := now + 30
dueResult, err := channels.ToggleSuggestedPostApproval(ctx, domain.ToggleSuggestedPostApprovalRequest{
UserID: owner.ID, MonoforumID: monoID, MessageID: due.Message.ID,
ScheduleDate: now - 1, Date: approvedAt,
})
if err != nil || dueResult.State != domain.SuggestedPostStateCompleted || dueResult.Published == nil {
t.Fatalf("due approval=%+v err=%v", dueResult, err)
}
if dueResult.OriginalMessage.SuggestedPost.ScheduleDate != approvedAt ||
dueResult.ServiceMessage.Action == nil ||
dueResult.ServiceMessage.Action.SuggestedPostScheduleDate != approvedAt {
t.Fatalf("due effective dates original/action=%d/%+v, want %d",
dueResult.OriginalMessage.SuggestedPost.ScheduleDate, dueResult.ServiceMessage.Action, approvedAt)
}
var persistedDate int
if err := pool.QueryRow(ctx, `
SELECT schedule_date
FROM suggested_post_approvals
WHERE monoforum_id=$1 AND suggestion_message_id=$2`, monoID, due.Message.ID).Scan(&persistedDate); err != nil {
t.Fatal(err)
}
if persistedDate != approvedAt {
t.Fatalf("persisted due schedule=%d, want %d", persistedDate, approvedAt)
}
}