115 lines
3.9 KiB
Go
115 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|