157 lines
6 KiB
Go
157 lines
6 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
func TestContactProjectionForViewerUserIDsPostgresDoesNotCrossPairs(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
suffix := randomSuffix(t)
|
|
users := NewUserStore(pool)
|
|
viewerA := createTestUser(t, ctx, users, "+1910"+suffix+"01", "Viewer", "A")
|
|
viewerB := createTestUser(t, ctx, users, "+1910"+suffix+"02", "Viewer", "B")
|
|
ownerA := createTestUser(t, ctx, users, "+1910"+suffix+"03", "Owner", "A")
|
|
ownerB := createTestUser(t, ctx, users, "+1910"+suffix+"04", "Owner", "B")
|
|
userIDs := []int64{viewerA.ID, viewerB.ID, ownerA.ID, ownerB.ID}
|
|
photoBase := time.Now().UnixNano() & 0x3fffffffffffffff
|
|
photoIDs := []int64{photoBase + 1, photoBase + 2, photoBase + 3, photoBase + 4}
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", userIDs)
|
|
_, _ = pool.Exec(ctx, "DELETE FROM photos WHERE id = ANY($1::bigint[])", photoIDs)
|
|
})
|
|
media := NewMediaStore(pool)
|
|
for _, photoID := range photoIDs {
|
|
if err := media.PutPhoto(ctx, domain.Photo{
|
|
ID: photoID, AccessHash: photoID + 100, FileReference: []byte("sparse-ref"), Date: 1700000000, DCID: 2,
|
|
Sizes: []domain.PhotoSize{{Kind: domain.PhotoSizeKindStripped, Type: "i", Bytes: []byte{1, 2, byte(photoID)}}},
|
|
}); err != nil {
|
|
t.Fatalf("PutPhoto(%d): %v", photoID, err)
|
|
}
|
|
}
|
|
contacts := NewContactStore(pool)
|
|
rows := []struct {
|
|
viewer int64
|
|
owner int64
|
|
name string
|
|
photo int64
|
|
}{
|
|
{viewerA.ID, ownerA.ID, "A expected", photoIDs[0]},
|
|
{viewerA.ID, ownerB.ID, "B cross", photoIDs[1]},
|
|
{viewerB.ID, ownerA.ID, "A cross", photoIDs[2]},
|
|
{viewerB.ID, ownerB.ID, "B expected", photoIDs[3]},
|
|
}
|
|
for _, row := range rows {
|
|
if _, err := contacts.Upsert(ctx, row.viewer, domain.ContactInput{
|
|
ContactUserID: row.owner,
|
|
FirstName: row.name,
|
|
Phone: "known-phone",
|
|
Note: "private note",
|
|
NoteEntities: []domain.MessageEntity{{
|
|
Type: domain.MessageEntityBold, Length: 7,
|
|
}},
|
|
}); err != nil {
|
|
t.Fatalf("Upsert %d->%d: %v", row.viewer, row.owner, err)
|
|
}
|
|
if _, found, err := contacts.SetPersonalPhoto(ctx, row.viewer, row.owner, row.photo, 1700000001); err != nil || !found {
|
|
t.Fatalf("SetPersonalPhoto %d->%d: found=%v err=%v", row.viewer, row.owner, found, err)
|
|
}
|
|
}
|
|
got, err := contacts.ContactProjectionForViewerUserIDs(ctx, map[int64][]int64{
|
|
viewerA.ID: {ownerA.ID},
|
|
viewerB.ID: {ownerB.ID},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ContactProjectionForViewerUserIDs: %v", err)
|
|
}
|
|
if len(got.Contacts[viewerA.ID]) != 1 || got.Contacts[viewerA.ID][ownerA.ID].FirstName != "A expected" {
|
|
t.Fatalf("viewer A contacts = %+v", got.Contacts[viewerA.ID])
|
|
}
|
|
contactA := got.Contacts[viewerA.ID][ownerA.ID]
|
|
if !reflect.DeepEqual(contactA.User, domain.User{ID: ownerA.ID}) {
|
|
t.Fatalf("viewer A sparse projection retained joined base user data: %+v", contactA.User)
|
|
}
|
|
if contactA.Phone != "known-phone" || contactA.Note != "private note" || len(contactA.NoteEntities) != 1 || contactA.NoteEntities[0].Length != 7 {
|
|
t.Fatalf("viewer A sparse overlay = %+v", contactA)
|
|
}
|
|
if len(got.Contacts[viewerB.ID]) != 1 || got.Contacts[viewerB.ID][ownerB.ID].FirstName != "B expected" {
|
|
t.Fatalf("viewer B contacts = %+v", got.Contacts[viewerB.ID])
|
|
}
|
|
if _, ok := got.Contacts[viewerA.ID][ownerB.ID]; ok {
|
|
t.Fatal("viewer A received crossed owner B")
|
|
}
|
|
if _, ok := got.Contacts[viewerB.ID][ownerA.ID]; ok {
|
|
t.Fatal("viewer B received crossed owner A")
|
|
}
|
|
if got.PersonalPhotos[viewerA.ID][ownerA.ID].PhotoID != photoIDs[0] || len(got.PersonalPhotos[viewerA.ID]) != 1 {
|
|
t.Fatalf("viewer A personal photos = %+v", got.PersonalPhotos[viewerA.ID])
|
|
}
|
|
if got.PersonalPhotos[viewerB.ID][ownerB.ID].PhotoID != photoIDs[3] || len(got.PersonalPhotos[viewerB.ID]) != 1 {
|
|
t.Fatalf("viewer B personal photos = %+v", got.PersonalPhotos[viewerB.ID])
|
|
}
|
|
}
|
|
|
|
func TestGetReverseContactsForViewerUserIDsPostgresDoesNotCrossPairs(t *testing.T) {
|
|
pool := testPool(t)
|
|
ctx := context.Background()
|
|
suffix := randomSuffix(t)
|
|
users := NewUserStore(pool)
|
|
viewerA := createTestUser(t, ctx, users, "+1911"+suffix+"01", "Viewer", "A")
|
|
viewerB := createTestUser(t, ctx, users, "+1911"+suffix+"02", "Viewer", "B")
|
|
ownerA := createTestUser(t, ctx, users, "+1911"+suffix+"03", "Owner", "A")
|
|
ownerB := createTestUser(t, ctx, users, "+1911"+suffix+"04", "Owner", "B")
|
|
userIDs := []int64{viewerA.ID, viewerB.ID, ownerA.ID, ownerB.ID}
|
|
t.Cleanup(func() {
|
|
_, _ = pool.Exec(ctx, "DELETE FROM users WHERE id = ANY($1::bigint[])", userIDs)
|
|
})
|
|
|
|
contacts := NewContactStore(pool)
|
|
for _, row := range []struct {
|
|
owner int64
|
|
viewer int64
|
|
name string
|
|
}{
|
|
{ownerA.ID, viewerA.ID, "A expected"},
|
|
{ownerA.ID, viewerB.ID, "A cross"},
|
|
{ownerB.ID, viewerA.ID, "B cross"},
|
|
{ownerB.ID, viewerB.ID, "B expected"},
|
|
} {
|
|
if _, err := contacts.Upsert(ctx, row.owner, domain.ContactInput{
|
|
ContactUserID: row.viewer,
|
|
FirstName: row.name,
|
|
Note: "relationship-only",
|
|
}); err != nil {
|
|
t.Fatalf("Upsert %d->%d: %v", row.owner, row.viewer, err)
|
|
}
|
|
}
|
|
if _, err := contacts.SetCloseFriends(ctx, ownerA.ID, []int64{viewerA.ID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := contacts.GetReverseContactsForViewerUserIDs(ctx, map[int64][]int64{
|
|
ownerA.ID: {viewerA.ID},
|
|
ownerB.ID: {viewerB.ID},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("GetReverseContactsForViewerUserIDs: %v", err)
|
|
}
|
|
contactA, found := got[ownerA.ID][viewerA.ID]
|
|
if !found || contactA.User.ID != viewerA.ID || contactA.FirstName != "A expected" || !contactA.CloseFriend {
|
|
t.Fatalf("owner A exact relationship = %+v found=%v", contactA, found)
|
|
}
|
|
contactB, found := got[ownerB.ID][viewerB.ID]
|
|
if !found || contactB.User.ID != viewerB.ID || contactB.FirstName != "B expected" || contactB.CloseFriend {
|
|
t.Fatalf("owner B exact relationship = %+v found=%v", contactB, found)
|
|
}
|
|
if _, found := got[ownerA.ID][viewerB.ID]; found {
|
|
t.Fatal("owner A received crossed viewer B")
|
|
}
|
|
if _, found := got[ownerB.ID][viewerA.ID]; found {
|
|
t.Fatal("owner B received crossed viewer A")
|
|
}
|
|
}
|