chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
587
internal/store/postgres/message_delete_integration_test.go
Normal file
587
internal/store/postgres/message_delete_integration_test.go
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"telesrv/internal/domain"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMessageStoreDeleteMessagesRecomputesRecipientUnreadAndTop(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
sender, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 61,
|
||||
Phone: "+1668" + suffix + "01",
|
||||
FirstName: "DeleteSender",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create sender: %v", err)
|
||||
}
|
||||
recipient, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 62,
|
||||
Phone: "+1668" + suffix + "02",
|
||||
FirstName: "DeleteRecipient",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create recipient: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{sender.ID, recipient.ID})
|
||||
})
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
bodies := []string{"one", "two", "three"}
|
||||
sent := make([]domain.SendPrivateTextResult, 0, len(bodies))
|
||||
for i, body := range bodies {
|
||||
got, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 824000 + int64(i),
|
||||
Message: body,
|
||||
Date: 1700000500 + i,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SendPrivateText %q: %v", body, err)
|
||||
}
|
||||
sent = append(sent, got)
|
||||
}
|
||||
|
||||
deleted, err := messages.DeleteMessages(ctx, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: sender.ID,
|
||||
IDs: []int{sent[2].SenderMessage.ID},
|
||||
Revoke: true,
|
||||
Date: 1700000510,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteMessages revoke latest: %v", err)
|
||||
}
|
||||
if len(deleted.Deleted) != 2 || !deleted.Changed() {
|
||||
t.Fatalf("deleted = %+v, want both owner boxes revoked", deleted)
|
||||
}
|
||||
|
||||
var unreadCount, actualUnread, topMessageID int
|
||||
var topBody string
|
||||
if err := pool.QueryRow(ctx, `
|
||||
WITH target AS (
|
||||
SELECT d.unread_count, d.top_message_id, d.read_inbox_max_id, COALESCE(m.body, '') AS top_body
|
||||
FROM dialogs d
|
||||
LEFT JOIN message_boxes m
|
||||
ON m.owner_user_id = d.user_id
|
||||
AND m.box_id = d.top_message_id
|
||||
AND NOT m.deleted
|
||||
WHERE d.user_id = $1
|
||||
AND d.peer_type = $2
|
||||
AND d.peer_id = $3
|
||||
),
|
||||
actual AS (
|
||||
SELECT COUNT(*)::int AS unread
|
||||
FROM message_boxes
|
||||
WHERE owner_user_id = $1
|
||||
AND peer_type = $2
|
||||
AND peer_id = $3
|
||||
AND NOT deleted
|
||||
AND NOT outgoing
|
||||
AND box_id > (SELECT read_inbox_max_id FROM target)
|
||||
)
|
||||
SELECT target.unread_count, target.top_message_id, target.top_body, actual.unread
|
||||
FROM target CROSS JOIN actual
|
||||
`, recipient.ID, string(domain.PeerTypeUser), sender.ID).Scan(&unreadCount, &topMessageID, &topBody, &actualUnread); err != nil {
|
||||
t.Fatalf("load recipient dialog after revoke: %v", err)
|
||||
}
|
||||
if unreadCount != 2 || actualUnread != 2 || topMessageID != sent[1].RecipientMessage.ID || topBody != "two" {
|
||||
t.Fatalf("recipient dialog after revoke = unread %d actual %d top %d body %q, want unread=2 top=%d body=two",
|
||||
unreadCount, actualUnread, topMessageID, topBody, sent[1].RecipientMessage.ID)
|
||||
}
|
||||
events, err := NewUpdateEventStore(pool).ListAfter(ctx, recipient.ID, 3, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("list recipient events after revoke: %v", err)
|
||||
}
|
||||
if len(events) != 1 ||
|
||||
events[0].Type != domain.UpdateEventDeleteMessages ||
|
||||
events[0].Pts != 4 ||
|
||||
events[0].PtsCount != 1 ||
|
||||
!sameInts(events[0].MessageIDs, []int{sent[2].RecipientMessage.ID}) {
|
||||
t.Fatalf("recipient events after revoke = %+v, want delete only; later unread messages are still live", events)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageStoreDeleteMiddleUnreadKeepsRecipientTop(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
sender, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 63,
|
||||
Phone: "+1669" + suffix + "01",
|
||||
FirstName: "MiddleDeleteSender",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create sender: %v", err)
|
||||
}
|
||||
recipient, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 64,
|
||||
Phone: "+1669" + suffix + "02",
|
||||
FirstName: "MiddleDeleteRecipient",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create recipient: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{sender.ID, recipient.ID})
|
||||
})
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
recipientPeer := domain.Peer{Type: domain.PeerTypeUser, ID: sender.ID}
|
||||
first, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 824999,
|
||||
Message: "already read",
|
||||
Date: 1700000590,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SendPrivateText first: %v", err)
|
||||
}
|
||||
if _, err := messages.ReadHistory(ctx, domain.ReadHistoryRequest{
|
||||
OwnerUserID: recipient.ID,
|
||||
Peer: recipientPeer,
|
||||
MaxID: first.RecipientMessage.ID,
|
||||
Date: 1700000595,
|
||||
}); err != nil {
|
||||
t.Fatalf("ReadHistory first: %v", err)
|
||||
}
|
||||
|
||||
bodies := []string{"one", "two", "three"}
|
||||
sent := make([]domain.SendPrivateTextResult, 0, len(bodies))
|
||||
for i, body := range bodies {
|
||||
got, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 825000 + int64(i),
|
||||
Message: body,
|
||||
Date: 1700000600 + i,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SendPrivateText %q: %v", body, err)
|
||||
}
|
||||
sent = append(sent, got)
|
||||
}
|
||||
|
||||
if _, err := messages.DeleteMessages(ctx, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: sender.ID,
|
||||
IDs: []int{sent[1].SenderMessage.ID},
|
||||
Revoke: true,
|
||||
Date: 1700000610,
|
||||
}); err != nil {
|
||||
t.Fatalf("DeleteMessages revoke middle: %v", err)
|
||||
}
|
||||
|
||||
var unreadCount, actualUnread, topMessageID, readInboxMaxID int
|
||||
var topBody string
|
||||
if err := pool.QueryRow(ctx, `
|
||||
WITH target AS (
|
||||
SELECT d.unread_count, d.top_message_id, d.read_inbox_max_id, COALESCE(m.body, '') AS top_body
|
||||
FROM dialogs d
|
||||
LEFT JOIN message_boxes m
|
||||
ON m.owner_user_id = d.user_id
|
||||
AND m.box_id = d.top_message_id
|
||||
AND NOT m.deleted
|
||||
WHERE d.user_id = $1
|
||||
AND d.peer_type = $2
|
||||
AND d.peer_id = $3
|
||||
),
|
||||
actual AS (
|
||||
SELECT COUNT(*)::int AS unread
|
||||
FROM message_boxes
|
||||
WHERE owner_user_id = $1
|
||||
AND peer_type = $2
|
||||
AND peer_id = $3
|
||||
AND NOT deleted
|
||||
AND NOT outgoing
|
||||
AND box_id > (SELECT read_inbox_max_id FROM target)
|
||||
)
|
||||
SELECT target.unread_count, target.top_message_id, target.read_inbox_max_id, target.top_body, actual.unread
|
||||
FROM target CROSS JOIN actual
|
||||
`, recipient.ID, string(domain.PeerTypeUser), sender.ID).Scan(&unreadCount, &topMessageID, &readInboxMaxID, &topBody, &actualUnread); err != nil {
|
||||
t.Fatalf("load recipient dialog after middle revoke: %v", err)
|
||||
}
|
||||
if unreadCount != 2 || actualUnread != 2 || topMessageID != sent[2].RecipientMessage.ID || readInboxMaxID != first.RecipientMessage.ID || topBody != "three" {
|
||||
t.Fatalf("recipient dialog after middle revoke = unread %d actual %d top %d read %d body %q, want unread=2 top=%d read=%d body=three",
|
||||
unreadCount, actualUnread, topMessageID, readInboxMaxID, topBody, sent[2].RecipientMessage.ID, first.RecipientMessage.ID)
|
||||
}
|
||||
events, err := NewUpdateEventStore(pool).ListAfter(ctx, recipient.ID, 5, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("list recipient events after middle revoke: %v", err)
|
||||
}
|
||||
if len(events) != 1 ||
|
||||
events[0].Type != domain.UpdateEventDeleteMessages ||
|
||||
events[0].Pts != 6 ||
|
||||
events[0].PtsCount != 1 ||
|
||||
!sameInts(events[0].MessageIDs, []int{sent[1].RecipientMessage.ID}) {
|
||||
t.Fatalf("recipient events after middle revoke = %+v, want delete only; first unread is still live", events)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageStoreDeleteFirstUnreadEmitsSafeUnreadCorrection(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
sender, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 65,
|
||||
Phone: "+1670" + suffix + "01",
|
||||
FirstName: "FirstUnreadDeleteSender",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create sender: %v", err)
|
||||
}
|
||||
recipient, err := users.Create(ctx, domain.User{
|
||||
AccessHash: 66,
|
||||
Phone: "+1670" + suffix + "02",
|
||||
FirstName: "FirstUnreadDeleteRecipient",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create recipient: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{sender.ID, recipient.ID})
|
||||
})
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
recipientPeer := domain.Peer{Type: domain.PeerTypeUser, ID: sender.ID}
|
||||
first, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 826000,
|
||||
Message: "already read",
|
||||
Date: 1700000640,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SendPrivateText first: %v", err)
|
||||
}
|
||||
if _, err := messages.ReadHistory(ctx, domain.ReadHistoryRequest{
|
||||
OwnerUserID: recipient.ID,
|
||||
Peer: recipientPeer,
|
||||
MaxID: first.RecipientMessage.ID,
|
||||
Date: 1700000645,
|
||||
}); err != nil {
|
||||
t.Fatalf("ReadHistory first: %v", err)
|
||||
}
|
||||
|
||||
bodies := []string{"one", "two", "three"}
|
||||
sent := make([]domain.SendPrivateTextResult, 0, len(bodies))
|
||||
for i, body := range bodies {
|
||||
got, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 826001 + int64(i),
|
||||
Message: body,
|
||||
Date: 1700000650 + i,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SendPrivateText %q: %v", body, err)
|
||||
}
|
||||
sent = append(sent, got)
|
||||
}
|
||||
|
||||
if _, err := messages.DeleteMessages(ctx, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: sender.ID,
|
||||
IDs: []int{sent[0].SenderMessage.ID},
|
||||
Revoke: true,
|
||||
Date: 1700000660,
|
||||
}); err != nil {
|
||||
t.Fatalf("DeleteMessages revoke first unread: %v", err)
|
||||
}
|
||||
|
||||
events, err := NewUpdateEventStore(pool).ListAfter(ctx, recipient.ID, 5, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("list recipient events after first unread revoke: %v", err)
|
||||
}
|
||||
if len(events) != 2 ||
|
||||
events[0].Type != domain.UpdateEventDeleteMessages ||
|
||||
events[0].Pts != 6 ||
|
||||
events[0].PtsCount != 1 ||
|
||||
!sameInts(events[0].MessageIDs, []int{sent[0].RecipientMessage.ID}) ||
|
||||
events[1].Type != domain.UpdateEventReadHistoryInbox ||
|
||||
events[1].Pts != 7 ||
|
||||
events[1].PtsCount != 1 ||
|
||||
events[1].MaxID != sent[0].RecipientMessage.ID ||
|
||||
events[1].StillUnreadCount != 2 {
|
||||
t.Fatalf("recipient events after first unread revoke = %+v, want delete then prefix unread correction", events)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageStoreDeleteHistoryRebuildsDialogAndEmitsDeleteUpdates(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
sender := createTestUser(t, ctx, users, "+1991"+suffix+"01", "DeleteSender", "")
|
||||
recipient := createTestUser(t, ctx, users, "+1991"+suffix+"02", "DeleteRecipient", "")
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{sender.ID, recipient.ID})
|
||||
})
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: int64(7000 + i),
|
||||
Message: "history",
|
||||
Date: 1700000700 + i,
|
||||
}); err != nil {
|
||||
t.Fatalf("seed send %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: recipient.ID}
|
||||
deleted, err := messages.DeleteHistory(ctx, domain.DeleteHistoryRequest{
|
||||
OwnerUserID: sender.ID,
|
||||
Peer: peer,
|
||||
Date: 1700000800,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteHistory: %v", err)
|
||||
}
|
||||
if self := deleted.Self(); self.Event.Pts != 4 || self.Event.PtsCount != 2 || len(self.MessageIDs) != 2 {
|
||||
t.Fatalf("delete result = %+v, want sender delete range pts=4 count=2 ids", self)
|
||||
}
|
||||
senderHistory, err := messages.ListByUser(ctx, sender.ID, domain.MessageFilter{HasPeer: true, Peer: peer, Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("sender history: %v", err)
|
||||
}
|
||||
recipientHistory, err := messages.ListByUser(ctx, recipient.ID, domain.MessageFilter{HasPeer: true, Peer: domain.Peer{Type: domain.PeerTypeUser, ID: sender.ID}, Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("recipient history: %v", err)
|
||||
}
|
||||
if len(senderHistory.Messages) != 0 || len(recipientHistory.Messages) != 2 {
|
||||
t.Fatalf("history sizes sender=%d recipient=%d, want sender cleared only", len(senderHistory.Messages), len(recipientHistory.Messages))
|
||||
}
|
||||
senderDialogs, err := NewDialogStore(pool).ListByUser(ctx, sender.ID, domain.DialogFilter{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("sender dialogs after delete: %v", err)
|
||||
}
|
||||
if len(senderDialogs.Dialogs) != 0 {
|
||||
t.Fatalf("sender dialogs = %+v, want empty after full history delete", senderDialogs.Dialogs)
|
||||
}
|
||||
events, err := NewUpdateEventStore(pool).ListAfter(ctx, sender.ID, 2, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("list sender events: %v", err)
|
||||
}
|
||||
if len(events) != 1 || events[0].Type != domain.UpdateEventDeleteMessages || events[0].Pts != 4 || events[0].PtsCount != 2 || len(events[0].MessageIDs) != 2 {
|
||||
t.Fatalf("events = %+v, want delete messages event pts=4 pts_count=2", events)
|
||||
}
|
||||
|
||||
rebuilt, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: sender.ID,
|
||||
RecipientUserID: recipient.ID,
|
||||
RandomID: 8000,
|
||||
Message: "after clear",
|
||||
Date: 1700000900,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("send after delete: %v", err)
|
||||
}
|
||||
senderDialogs, err = NewDialogStore(pool).ListByUser(ctx, sender.ID, domain.DialogFilter{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("sender dialogs after rebuild: %v", err)
|
||||
}
|
||||
if len(senderDialogs.Dialogs) != 1 || senderDialogs.Dialogs[0].Peer != peer || senderDialogs.Dialogs[0].TopMessage != rebuilt.SenderMessage.ID {
|
||||
t.Fatalf("rebuilt dialogs = %+v, want new top message %d", senderDialogs.Dialogs, rebuilt.SenderMessage.ID)
|
||||
}
|
||||
|
||||
revoked, err := messages.DeleteMessages(ctx, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: sender.ID,
|
||||
IDs: []int{rebuilt.SenderMessage.ID},
|
||||
Revoke: true,
|
||||
Date: 1700001000,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteMessages revoke: %v", err)
|
||||
}
|
||||
if len(revoked.Deleted) != 2 || !revoked.Changed() {
|
||||
t.Fatalf("revoked = %+v, want delete events for both owners", revoked)
|
||||
}
|
||||
senderHistory, err = messages.ListByUser(ctx, sender.ID, domain.MessageFilter{HasPeer: true, Peer: peer, Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("sender history after revoke: %v", err)
|
||||
}
|
||||
recipientHistory, err = messages.ListByUser(ctx, recipient.ID, domain.MessageFilter{HasPeer: true, Peer: domain.Peer{Type: domain.PeerTypeUser, ID: sender.ID}, Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("recipient history after revoke: %v", err)
|
||||
}
|
||||
if len(senderHistory.Messages) != 0 || len(recipientHistory.Messages) != 2 {
|
||||
t.Fatalf("history sizes after revoke sender=%d recipient=%d, want new message removed from both owners", len(senderHistory.Messages), len(recipientHistory.Messages))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageStoreDeleteHistoryJustClearPreservesEmptyDialog(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
owner := createTestUser(t, ctx, users, "+1992"+suffix+"01", "ClearOwner", "")
|
||||
peerUser := createTestUser(t, ctx, users, "+1992"+suffix+"02", "ClearPeer", "")
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{owner.ID, peerUser.ID})
|
||||
})
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
if _, err := messages.SendPrivateText(ctx, domain.SendPrivateTextRequest{
|
||||
SenderUserID: owner.ID,
|
||||
RecipientUserID: peerUser.ID,
|
||||
RandomID: 9000,
|
||||
Message: "clear but keep dialog",
|
||||
Date: 1700001100,
|
||||
}); err != nil {
|
||||
t.Fatalf("seed send: %v", err)
|
||||
}
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: peerUser.ID}
|
||||
if _, err := messages.DeleteHistory(ctx, domain.DeleteHistoryRequest{
|
||||
OwnerUserID: owner.ID,
|
||||
Peer: peer,
|
||||
JustClear: true,
|
||||
Date: 1700001200,
|
||||
}); err != nil {
|
||||
t.Fatalf("DeleteHistory just_clear: %v", err)
|
||||
}
|
||||
dialogs, err := NewDialogStore(pool).ListByUser(ctx, owner.ID, domain.DialogFilter{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("dialogs after just_clear: %v", err)
|
||||
}
|
||||
if len(dialogs.Dialogs) != 1 || dialogs.Dialogs[0].Peer != peer || dialogs.Dialogs[0].TopMessage != 0 || len(dialogs.Messages) != 0 {
|
||||
t.Fatalf("dialogs = %+v messages=%+v, want empty dialog preserved after just_clear", dialogs.Dialogs, dialogs.Messages)
|
||||
}
|
||||
history, err := messages.ListByUser(ctx, owner.ID, domain.MessageFilter{HasPeer: true, Peer: peer, Limit: 10, NeedTotalCount: true})
|
||||
if err != nil {
|
||||
t.Fatalf("history after just_clear: %v", err)
|
||||
}
|
||||
if len(history.Messages) != 0 {
|
||||
t.Fatalf("history = %+v, want cleared", history.Messages)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageStoreDeleteHistoryBatchesHugeMaxID(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
suffix := randomSuffix(t)
|
||||
|
||||
users := NewUserStore(pool)
|
||||
owner := createTestUser(t, ctx, users, "+1993"+suffix+"01", "BulkOwner", "")
|
||||
peerUser := createTestUser(t, ctx, users, "+1993"+suffix+"02", "BulkPeer", "")
|
||||
t.Cleanup(func() {
|
||||
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{owner.ID, peerUser.ID})
|
||||
})
|
||||
|
||||
total := domain.MaxDeleteHistoryBatch + 2
|
||||
if _, err := pool.Exec(ctx, `
|
||||
WITH src AS (
|
||||
SELECT generate_series(1, $3::int) AS g
|
||||
),
|
||||
pm AS (
|
||||
INSERT INTO private_messages (
|
||||
sender_user_id,
|
||||
recipient_user_id,
|
||||
random_id,
|
||||
message_date,
|
||||
body,
|
||||
entities
|
||||
)
|
||||
SELECT
|
||||
$1::bigint,
|
||||
$2::bigint,
|
||||
910000000 + g,
|
||||
1700002000 + g,
|
||||
'bulk history',
|
||||
'[]'::jsonb
|
||||
FROM src
|
||||
RETURNING id, random_id, message_date
|
||||
)
|
||||
INSERT INTO message_boxes (
|
||||
owner_user_id,
|
||||
box_id,
|
||||
private_message_id,
|
||||
message_sender_id,
|
||||
peer_type,
|
||||
peer_id,
|
||||
from_user_id,
|
||||
message_date,
|
||||
outgoing,
|
||||
body,
|
||||
entities,
|
||||
pts
|
||||
)
|
||||
SELECT
|
||||
$1::bigint,
|
||||
(random_id - 910000000)::int,
|
||||
id,
|
||||
$1::bigint,
|
||||
'user',
|
||||
$2::bigint,
|
||||
$1::bigint,
|
||||
message_date,
|
||||
true,
|
||||
'bulk history',
|
||||
'[]'::jsonb,
|
||||
0
|
||||
FROM pm
|
||||
`, owner.ID, peerUser.ID, total); err != nil {
|
||||
t.Fatalf("seed bulk history: %v", err)
|
||||
}
|
||||
if _, err := pool.Exec(ctx, `
|
||||
INSERT INTO dialogs (
|
||||
user_id,
|
||||
peer_type,
|
||||
peer_id,
|
||||
top_message_id,
|
||||
top_message_date,
|
||||
read_outbox_max_id,
|
||||
unread_count
|
||||
) VALUES ($1, 'user', $2, $3, $4, $3, 0)
|
||||
`, owner.ID, peerUser.ID, total, 1700002000+total); err != nil {
|
||||
t.Fatalf("seed dialog: %v", err)
|
||||
}
|
||||
|
||||
messages := NewMessageStore(pool)
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: peerUser.ID}
|
||||
first, err := messages.DeleteHistory(ctx, domain.DeleteHistoryRequest{
|
||||
OwnerUserID: owner.ID,
|
||||
Peer: peer,
|
||||
MaxID: domain.MaxMessageBoxID,
|
||||
Date: 1700003000,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteHistory first batch: %v", err)
|
||||
}
|
||||
self := first.Self()
|
||||
if first.Offset != 1 || self.Event.Pts != domain.MaxDeleteHistoryBatch || self.Event.PtsCount != domain.MaxDeleteHistoryBatch || len(self.MessageIDs) != domain.MaxDeleteHistoryBatch {
|
||||
t.Fatalf("first batch = %+v self=%+v, want offset=1 and exactly %d deleted ids", first, self, domain.MaxDeleteHistoryBatch)
|
||||
}
|
||||
history, err := messages.ListByUser(ctx, owner.ID, domain.MessageFilter{HasPeer: true, Peer: peer, Limit: 10, NeedTotalCount: true})
|
||||
if err != nil {
|
||||
t.Fatalf("history after first batch: %v", err)
|
||||
}
|
||||
if history.Count != 2 || len(history.Messages) != 2 || history.Messages[0].ID != 2 {
|
||||
t.Fatalf("history after first batch = %+v, want only two oldest messages left", history)
|
||||
}
|
||||
|
||||
second, err := messages.DeleteHistory(ctx, domain.DeleteHistoryRequest{
|
||||
OwnerUserID: owner.ID,
|
||||
Peer: peer,
|
||||
MaxID: domain.MaxMessageBoxID,
|
||||
Date: 1700003001,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteHistory second batch: %v", err)
|
||||
}
|
||||
if second.Offset != 0 || second.Self().Event.PtsCount != 2 {
|
||||
t.Fatalf("second batch = %+v, want final offset=0 pts_count=2", second)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue