190 lines
6.4 KiB
Go
190 lines
6.4 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
func TestChannelDialogSnapshotHashTracksDependenciesWithoutWideHeaders(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
suffix := randomSuffix(t)
|
|
|
|
owner, err := NewUserStore(pool).Create(ctx, domain.User{
|
|
AccessHash: 51,
|
|
Phone: "+1778" + suffix + "01",
|
|
FirstName: "SnapshotHashOwner",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
created, err := NewChannelStore(pool).CreateChannel(ctx, domain.CreateChannelRequest{
|
|
CreatorUserID: owner.ID,
|
|
Title: "Snapshot Hash " + suffix,
|
|
Megagroup: true,
|
|
Date: 1700000600,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create channel: %v", err)
|
|
}
|
|
channelID := created.Channel.ID
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, "DELETE FROM channels WHERE id = $1", channelID)
|
|
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = $1", owner.ID)
|
|
})
|
|
|
|
channels := NewChannelStore(pool)
|
|
load := func() domain.ChannelDialogList {
|
|
t.Helper()
|
|
list, err := channels.ListChannelDialogSnapshotHeaders(ctx, owner.ID, domain.DialogFilter{})
|
|
if err != nil {
|
|
t.Fatalf("list snapshot headers: %v", err)
|
|
}
|
|
if len(list.Dialogs) != 1 || list.Hash == 0 {
|
|
t.Fatalf("snapshot = %+v, want one dialog and non-zero hash", list)
|
|
}
|
|
dialog := list.Dialogs[0]
|
|
if dialog.Peer.ID != channelID || dialog.TopMessage == 0 || dialog.TopMessageDate == 0 {
|
|
t.Fatalf("ordering header = %+v, want peer/top/date", dialog)
|
|
}
|
|
if dialog.ReadInboxMaxID != 0 || dialog.ReadOutboxMaxID != 0 || dialog.UnreadCount != 0 ||
|
|
dialog.UnreadMentions != 0 || dialog.UnreadReactions != 0 || dialog.Pts != 0 {
|
|
t.Fatalf("snapshot header retained mutable hydration fields: %+v", dialog)
|
|
}
|
|
return list
|
|
}
|
|
|
|
before := load()
|
|
changed, err := channels.SetChannelDialogUnreadMark(ctx, owner.ID, channelID, true)
|
|
if err != nil || !changed {
|
|
t.Fatalf("set unread mark = %v, %v", changed, err)
|
|
}
|
|
afterOwnerState := load()
|
|
if afterOwnerState.Hash == before.Hash {
|
|
t.Fatalf("owner-local dependency hash stayed %d after unread mark", before.Hash)
|
|
}
|
|
if afterOwnerState.Dialogs[0].TopMessage != before.Dialogs[0].TopMessage ||
|
|
afterOwnerState.Dialogs[0].TopMessageDate != before.Dialogs[0].TopMessageDate {
|
|
t.Fatalf("unread mark changed ordering header: before=%+v after=%+v", before.Dialogs[0], afterOwnerState.Dialogs[0])
|
|
}
|
|
|
|
if _, err := pool.Exec(ctx, "UPDATE channels SET title = title || ' changed' WHERE id = $1", channelID); err != nil {
|
|
t.Fatalf("update channel title: %v", err)
|
|
}
|
|
afterSharedState := load()
|
|
if afterSharedState.Hash == afterOwnerState.Hash {
|
|
t.Fatalf("shared channel dependency hash stayed %d after channel-base change", afterOwnerState.Hash)
|
|
}
|
|
}
|
|
|
|
func TestPrivateDialogSnapshotHashTracksDraftDependency(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
suffix := randomSuffix(t)
|
|
users := NewUserStore(pool)
|
|
owner, err := users.Create(ctx, domain.User{
|
|
AccessHash: 52,
|
|
Phone: "+1778" + suffix + "02",
|
|
FirstName: "PrivateSnapshotOwner",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create owner: %v", err)
|
|
}
|
|
peer, err := users.Create(ctx, domain.User{
|
|
AccessHash: 53,
|
|
Phone: "+1778" + suffix + "03",
|
|
FirstName: "PrivateSnapshotPeer",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create peer: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{owner.ID, peer.ID})
|
|
})
|
|
|
|
dialogs := NewDialogStore(pool)
|
|
dialogPeer := domain.Peer{Type: domain.PeerTypeUser, ID: peer.ID}
|
|
if err := dialogs.Upsert(ctx, owner.ID, domain.Dialog{
|
|
Peer: dialogPeer,
|
|
TopMessage: 7,
|
|
TopMessageDate: 1700000610,
|
|
}); err != nil {
|
|
t.Fatalf("upsert dialog: %v", err)
|
|
}
|
|
before, err := dialogs.ListDialogSnapshotHeaders(ctx, owner.ID, domain.DialogFilter{})
|
|
if err != nil {
|
|
t.Fatalf("list snapshot before draft: %v", err)
|
|
}
|
|
if len(before.Dialogs) != 1 || before.Hash == 0 {
|
|
t.Fatalf("snapshot before draft = %+v", before)
|
|
}
|
|
if err := dialogs.SaveDraft(ctx, owner.ID, domain.DialogDraft{
|
|
Peer: dialogPeer,
|
|
Date: 1700000611,
|
|
Message: "draft changes hash without changing ordering",
|
|
}); err != nil {
|
|
t.Fatalf("save draft: %v", err)
|
|
}
|
|
after, err := dialogs.ListDialogSnapshotHeaders(ctx, owner.ID, domain.DialogFilter{})
|
|
if err != nil {
|
|
t.Fatalf("list snapshot after draft: %v", err)
|
|
}
|
|
if after.Hash == before.Hash {
|
|
t.Fatalf("private snapshot hash stayed %d after draft change", before.Hash)
|
|
}
|
|
if after.Dialogs[0].TopMessage != before.Dialogs[0].TopMessage || after.Dialogs[0].TopMessageDate != before.Dialogs[0].TopMessageDate {
|
|
t.Fatalf("draft changed ordering header: before=%+v after=%+v", before.Dialogs[0], after.Dialogs[0])
|
|
}
|
|
}
|
|
|
|
func TestPrivateDialogAllBuiltinSnapshotIncludesMainAndArchive(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
suffix := randomSuffix(t)
|
|
users := NewUserStore(pool)
|
|
owner, err := users.Create(ctx, domain.User{AccessHash: 61, Phone: "+1778" + suffix + "11", FirstName: "AllFolderOwner"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mainPeer, err := users.Create(ctx, domain.User{AccessHash: 62, Phone: "+1778" + suffix + "12", FirstName: "MainPeer"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
archivePeer, err := users.Create(ctx, domain.User{AccessHash: 63, Phone: "+1778" + suffix + "13", FirstName: "ArchivePeer"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", []int64{owner.ID, mainPeer.ID, archivePeer.ID})
|
|
})
|
|
dialogs := NewDialogStore(pool)
|
|
for index, peer := range []int64{mainPeer.ID, archivePeer.ID} {
|
|
if err := dialogs.Upsert(ctx, owner.ID, domain.Dialog{
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: peer},
|
|
TopMessage: 10 + index, TopMessageDate: 1700000700 + index,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := dialogs.EditPeerFolders(ctx, owner.ID, []domain.FolderPeerUpdate{{
|
|
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: archivePeer.ID}, FolderID: domain.DialogArchiveFolderID,
|
|
}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
all, err := dialogs.ListAllBuiltinDialogSnapshotHeaders(ctx, owner.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(all.Dialogs) != 2 {
|
|
t.Fatalf("all built-in private dialogs = %+v", all.Dialogs)
|
|
}
|
|
folders := map[int64]int{}
|
|
for _, dialog := range all.Dialogs {
|
|
folders[dialog.Peer.ID] = dialog.FolderID
|
|
}
|
|
if folders[mainPeer.ID] != domain.DialogMainFolderID || folders[archivePeer.ID] != domain.DialogArchiveFolderID {
|
|
t.Fatalf("all built-in private folders = %#v", folders)
|
|
}
|
|
}
|