Ctrl K
ComunidadGuías…

internal/store/postgres/contact_sparse_integration_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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")
	}
}