Ctrl K
ComunidadGuías…

internal/app/userprojection/projection_sparse_test.go

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

Archivo original ↗Preguntar a KiBot
package userprojection

import (
	"context"
	"reflect"
	"testing"

	privacyapp "telesrv/internal/app/privacy"
	"telesrv/internal/domain"
	"telesrv/internal/store"
	"telesrv/internal/store/memory"
)

type recordingSparseContactStore struct {
	store.ContactStore
	sparseCalls int
	denseCalls  int
	requested   map[int64][]int64
}

func (s *recordingSparseContactStore) ContactProjectionForViewers(ctx context.Context, viewers, owners []int64) (domain.ContactProjectionBatch, error) {
	s.denseCalls++
	return s.ContactStore.ContactProjectionForViewers(ctx, viewers, owners)
}

func (s *recordingSparseContactStore) ContactProjectionForViewerUserIDs(ctx context.Context, requested map[int64][]int64) (domain.ContactProjectionBatch, error) {
	s.sparseCalls++
	s.requested = make(map[int64][]int64, len(requested))
	for viewerID, ids := range requested {
		s.requested[viewerID] = append([]int64(nil), ids...)
	}
	return s.ContactStore.(store.SparseContactProjectionStore).ContactProjectionForViewerUserIDs(ctx, requested)
}

func TestForViewerUserIDsUsesActualPairsAndMatchesScalarProjection(t *testing.T) {
	ctx := context.Background()
	const (
		viewerA = int64(1101)
		viewerB = int64(1102)
		ownerA  = int64(2101)
		ownerB  = int64(2102)
	)
	contacts := memory.NewContactStore()
	// Seed both requested and cross-viewer rows. A dense matrix would expose the
	// cross aliases/photos; the sparse request must never ask for those pairs.
	for _, input := range []struct {
		viewer int64
		owner  int64
		name   string
		photo  int64
	}{
		{viewerA, ownerA, "A for viewer A", 9101},
		{viewerA, ownerB, "B cross leak", 9191},
		{viewerB, ownerB, "B for viewer B", 9102},
		{viewerB, ownerA, "A cross leak", 9192},
		// Reverse rows are the privacy ViewerIsContact facts.
		{ownerA, viewerA, "viewer A", 0},
		{ownerB, viewerB, "viewer B", 0},
	} {
		if _, err := contacts.Upsert(ctx, input.viewer, domain.ContactInput{ContactUserID: input.owner, FirstName: input.name}); err != nil {
			t.Fatalf("upsert %d->%d: %v", input.viewer, input.owner, err)
		}
		if input.photo != 0 {
			if _, _, err := contacts.SetPersonalPhoto(ctx, input.viewer, input.owner, input.photo, 100); err != nil {
				t.Fatalf("personal photo %d->%d: %v", input.viewer, input.owner, err)
			}
		}
	}
	recording := &recordingSparseContactStore{ContactStore: contacts}
	privacy := privacyapp.NewService(memory.NewPrivacyStore(), recording)
	projector := New(WithContactStore(recording), WithPrivacyEvaluator(privacy))
	base := []domain.User{
		{ID: ownerA, AccessHash: 31, Phone: "15552101", FirstName: "Owner A"},
		{ID: ownerB, AccessHash: 32, Phone: "15552102", FirstName: "Owner B"},
	}
	wantA, err := projector.ForViewer(ctx, viewerA, base[:1])
	if err != nil {
		t.Fatalf("scalar viewer A: %v", err)
	}
	wantB, err := projector.ForViewer(ctx, viewerB, base[1:])
	if err != nil {
		t.Fatalf("scalar viewer B: %v", err)
	}
	recording.sparseCalls = 0
	recording.denseCalls = 0
	recording.requested = nil

	got, err := projector.ForViewerUserIDs(ctx, map[int64][]int64{
		viewerA: {ownerA},
		viewerB: {ownerB},
	}, base)
	if err != nil {
		t.Fatalf("ForViewerUserIDs: %v", err)
	}
	if !reflect.DeepEqual(got[viewerA], wantA) || !reflect.DeepEqual(got[viewerB], wantB) {
		t.Fatalf("sparse projection = %+v, want scalar A=%+v B=%+v", got, wantA, wantB)
	}
	if recording.sparseCalls != 1 || recording.denseCalls != 0 {
		t.Fatalf("contact projection calls = sparse %d dense %d, want 1/0", recording.sparseCalls, recording.denseCalls)
	}
	wantPairs := map[int64][]int64{
		viewerA: {ownerA},
		viewerB: {ownerB},
		ownerA:  {viewerA},
		ownerB:  {viewerB},
	}
	for viewerID, ids := range wantPairs {
		if !reflect.DeepEqual(recording.requested[viewerID], ids) {
			t.Fatalf("requested[%d] = %v, want %v (all=%+v)", viewerID, recording.requested[viewerID], ids, recording.requested)
		}
	}
	if len(recording.requested) != len(wantPairs) {
		t.Fatalf("requested pairs = %+v, contains unexpected cross-viewer edges", recording.requested)
	}
}