599 lines
20 KiB
Go
599 lines
20 KiB
Go
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,
|
|
request_fingerprint,
|
|
recipient_delivered,
|
|
sender_box_id,
|
|
sender_pts,
|
|
recipient_box_id,
|
|
recipient_pts,
|
|
message_date,
|
|
body,
|
|
entities
|
|
)
|
|
SELECT
|
|
$1::bigint,
|
|
$2::bigint,
|
|
910000000 + g,
|
|
decode(repeat('00', 32), 'hex'),
|
|
false,
|
|
g,
|
|
g,
|
|
0,
|
|
0,
|
|
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,
|
|
(random_id - 910000000)::int
|
|
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)
|
|
}
|
|
}
|