merged from gramsrv upstream
This commit is contained in:
parent
79c64ee916
commit
21a0856587
651 changed files with 54774 additions and 4590 deletions
|
|
@ -2,6 +2,8 @@ package rpc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/iamxvbaba/td/clock"
|
||||
|
|
@ -9,11 +11,121 @@ import (
|
|||
|
||||
appchannels "telesrv/internal/app/channels"
|
||||
appdialogs "telesrv/internal/app/dialogs"
|
||||
appmessages "telesrv/internal/app/messages"
|
||||
appusers "telesrv/internal/app/users"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
func TestEnrichMessageListReusesApplicationViewerProjection(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const (
|
||||
viewerID = int64(1001)
|
||||
peerID = int64(1002)
|
||||
viaBotID = int64(1003)
|
||||
)
|
||||
users := &countingMapUsersService{mapUsersService: mapUsersService{users: map[int64]domain.User{
|
||||
viewerID: {ID: viewerID, FirstName: "Viewer"},
|
||||
peerID: {ID: peerID, FirstName: "Peer"},
|
||||
viaBotID: {ID: viaBotID, FirstName: "Bot", Bot: true},
|
||||
}}}
|
||||
r := New(Config{}, Deps{
|
||||
Messages: newCompletePeerCacheMessageService(),
|
||||
Users: users,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
list := domain.MessageList{
|
||||
Messages: []domain.Message{{
|
||||
OwnerUserID: viewerID,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: peerID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: viewerID},
|
||||
ViaBotID: viaBotID,
|
||||
}},
|
||||
Users: []domain.User{
|
||||
{ID: viewerID, FirstName: "Viewer"},
|
||||
{ID: peerID, FirstName: "Peer"},
|
||||
},
|
||||
}
|
||||
got := r.enrichMessageList(ctx, viewerID, list)
|
||||
if users.byIDsCalls != 1 || len(users.lastByIDs) != 1 || users.lastByIDs[0] != viaBotID {
|
||||
t.Fatalf("ByIDs calls=%d ids=%v, want only missing nested bot %d", users.byIDsCalls, users.lastByIDs, viaBotID)
|
||||
}
|
||||
if len(got.Users) != 3 {
|
||||
t.Fatalf("users=%+v, want projected envelope plus nested bot", got.Users)
|
||||
}
|
||||
|
||||
users.byIDsCalls = 0
|
||||
users.lastByIDs = nil
|
||||
list.Users = append(list.Users, domain.User{ID: viaBotID, FirstName: "Bot", Bot: true})
|
||||
got = r.enrichMessageList(ctx, viewerID, list)
|
||||
if users.byIDsCalls != 0 {
|
||||
t.Fatalf("ByIDs calls with complete projected envelope=%d, want 0", users.byIDsCalls)
|
||||
}
|
||||
if len(got.Users) != 3 {
|
||||
t.Fatalf("complete users=%+v, want 3", got.Users)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnrichMessageListDoesNotTrustPartialApplicationProjection(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const (
|
||||
viewerID = int64(1101)
|
||||
peerID = int64(1102)
|
||||
)
|
||||
users := &countingMapUsersService{mapUsersService: mapUsersService{users: map[int64]domain.User{
|
||||
viewerID: {ID: viewerID, FirstName: "Projected viewer"},
|
||||
peerID: {ID: peerID, FirstName: "Projected peer"},
|
||||
}}}
|
||||
r := New(Config{}, Deps{
|
||||
Messages: appmessages.NewService(memory.NewMessageStore(), nil),
|
||||
Users: users,
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
got := r.enrichMessageList(ctx, viewerID, domain.MessageList{
|
||||
Messages: []domain.Message{{
|
||||
OwnerUserID: viewerID,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: peerID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: viewerID},
|
||||
}},
|
||||
Users: []domain.User{{ID: viewerID, FirstName: "Raw viewer"}, {ID: peerID, FirstName: "Raw peer"}},
|
||||
})
|
||||
if users.byIDsCalls != 1 || len(users.lastByIDs) != 2 {
|
||||
t.Fatalf("ByIDs calls=%d ids=%v, want one authoritative reload of both ordinary refs", users.byIDsCalls, users.lastByIDs)
|
||||
}
|
||||
for _, user := range got.Users {
|
||||
if user.ID == peerID && user.FirstName != "Projected peer" {
|
||||
t.Fatalf("peer=%+v, want RPC projection to replace the untrusted raw envelope", user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newCompletePeerCacheMessageService() *appmessages.Service {
|
||||
return appmessages.NewService(memory.NewMessageStore(), nil,
|
||||
appmessages.WithContactStore(memory.NewContactStore()),
|
||||
appmessages.WithPhotoProvider(peerCacheTestPhotos{}),
|
||||
appmessages.WithPrivacyEvaluator(peerCacheTestPrivacy{}),
|
||||
appmessages.WithAccountFreezeProvider(peerCacheTestFreezes{}),
|
||||
)
|
||||
}
|
||||
|
||||
type peerCacheTestPhotos struct{}
|
||||
|
||||
func (peerCacheTestPhotos) CurrentProfilePhotos(context.Context, domain.PeerType, []int64) (map[int64]domain.ProfilePhotoRef, error) {
|
||||
return map[int64]domain.ProfilePhotoRef{}, nil
|
||||
}
|
||||
|
||||
type peerCacheTestPrivacy struct{}
|
||||
|
||||
func (peerCacheTestPrivacy) CanSee(context.Context, int64, int64, domain.PrivacyKey) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type peerCacheTestFreezes struct{}
|
||||
|
||||
func (peerCacheTestFreezes) AccountFreezes(context.Context, []int64) (map[int64]domain.AccountFreeze, error) {
|
||||
return map[int64]domain.AccountFreeze{}, nil
|
||||
}
|
||||
|
||||
func TestViewerPeerCacheChannelsForIDsUsesBatchAndCachesMissing(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
|
|
@ -81,3 +193,146 @@ func TestViewerPeerCacheChannelsForIDsUsesBatchAndCachesMissing(t *testing.T) {
|
|||
t.Fatalf("cached missing calls: GetChannels=%d GetChannel=%d, want no extra calls", counting.getChannelsCalls, counting.getChannelCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestViewerPeerCacheChunksLargeUserUnionsWithoutTruncation(t *testing.T) {
|
||||
const viewerID = int64(9001)
|
||||
ids := make([]int64, maxPeerProjectionUsersPerBatch+37)
|
||||
base := make(map[int64]domain.User, len(ids))
|
||||
for i := range ids {
|
||||
ids[i] = int64(10000 + i)
|
||||
base[ids[i]] = domain.User{ID: ids[i], FirstName: "projected"}
|
||||
}
|
||||
users := &countingMapUsersService{mapUsersService: mapUsersService{users: base}}
|
||||
router := New(Config{}, Deps{Users: users}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
got := newViewerPeerCache(router).usersForIDs(context.Background(), viewerID, ids)
|
||||
if len(got) != len(ids) {
|
||||
t.Fatalf("projected users=%d, want all %d", len(got), len(ids))
|
||||
}
|
||||
if users.byIDsCalls != 2 || len(users.byIDsBatches) != 2 {
|
||||
t.Fatalf("ByIDs calls=%d batches=%d, want two bounded batches", users.byIDsCalls, len(users.byIDsBatches))
|
||||
}
|
||||
for i, batch := range users.byIDsBatches {
|
||||
if len(batch) == 0 || len(batch) > maxPeerProjectionUsersPerBatch {
|
||||
t.Fatalf("batch %d size=%d, want 1..%d", i, len(batch), maxPeerProjectionUsersPerBatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type capacityBudgetPeerUsers struct {
|
||||
mapUsersService
|
||||
maxBatch int
|
||||
calls [][]int64
|
||||
}
|
||||
|
||||
func (s *capacityBudgetPeerUsers) ByIDs(_ context.Context, _ int64, ids []int64) ([]domain.User, error) {
|
||||
s.calls = append(s.calls, append([]int64(nil), ids...))
|
||||
if s.maxBatch > 0 && len(ids) > s.maxBatch {
|
||||
return nil, fmt.Errorf("%w: test batch size %d", appusers.ErrBatchViewerCells, len(ids))
|
||||
}
|
||||
out := make([]domain.User, len(ids))
|
||||
for i, id := range ids {
|
||||
out[i] = domain.User{ID: id, FirstName: "projected"}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func TestViewerPeerCacheCapacityRecoveryBudgetIsSharedAndFailClosed(t *testing.T) {
|
||||
ids := func(count int) []int64 {
|
||||
out := make([]int64, count)
|
||||
for i := range out {
|
||||
out[i] = int64(20_000 + i)
|
||||
}
|
||||
return out
|
||||
}
|
||||
newCache := func(users *capacityBudgetPeerUsers) *viewerPeerCache {
|
||||
router := New(Config{}, Deps{Users: users}, zaptest.NewLogger(t), clock.System)
|
||||
return newViewerPeerCache(router)
|
||||
}
|
||||
|
||||
t.Run("bounded split completes below call limit", func(t *testing.T) {
|
||||
users := &capacityBudgetPeerUsers{maxBatch: 32}
|
||||
got, err := newCache(users).usersForIDsStrict(context.Background(), 9001, ids(maxPeerProjectionUsersPerBatch))
|
||||
if err != nil || len(got) != maxPeerProjectionUsersPerBatch {
|
||||
t.Fatalf("strict users=%d err=%v, want complete projection", len(got), err)
|
||||
}
|
||||
if len(users.calls) != 63 {
|
||||
t.Fatalf("resolver calls=%d, want balanced 63-call recovery below limit %d", len(users.calls), maxPeerProjectionRecoveryCalls)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("call budget rejects whole projection", func(t *testing.T) {
|
||||
users := &capacityBudgetPeerUsers{maxBatch: 16}
|
||||
got, err := newCache(users).usersForIDsStrict(context.Background(), 9001, ids(maxPeerProjectionUsersPerBatch))
|
||||
if got != nil || !errors.Is(err, ErrUserProjectionCapacityRecoveryLimit) {
|
||||
t.Fatalf("strict users=%+v err=%v, want nil recovery-limit error", got, err)
|
||||
}
|
||||
if errors.Is(err, appusers.ErrBatchViewerCells) {
|
||||
t.Fatalf("recovery-limit error must not retain capacity identity: %v", err)
|
||||
}
|
||||
if len(users.calls) != maxPeerProjectionRecoveryCalls {
|
||||
t.Fatalf("resolver calls=%d, want hard limit %d", len(users.calls), maxPeerProjectionRecoveryCalls)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("attempted owner budget spans outer chunks", func(t *testing.T) {
|
||||
users := &capacityBudgetPeerUsers{}
|
||||
got, err := newCache(users).usersForIDsStrict(context.Background(), 9001, ids(maxPeerProjectionAttemptedOwnerIDs+1))
|
||||
if got != nil || !errors.Is(err, ErrUserProjectionCapacityRecoveryLimit) {
|
||||
t.Fatalf("strict users=%+v err=%v, want nil recovery-limit error", got, err)
|
||||
}
|
||||
wantCalls := maxPeerProjectionAttemptedOwnerIDs / maxPeerProjectionUsersPerBatch
|
||||
if len(users.calls) != wantCalls {
|
||||
t.Fatalf("resolver calls=%d, want %d successful chunks before shared owner limit", len(users.calls), wantCalls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestWithDialogListPresenceOnlyLoadsMissingMessagePeers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
const (
|
||||
viewerID = int64(1001)
|
||||
peerID = int64(1002)
|
||||
viaBotID = int64(1003)
|
||||
)
|
||||
users := &countingMapUsersService{mapUsersService: mapUsersService{users: map[int64]domain.User{
|
||||
viewerID: {ID: viewerID, FirstName: "Viewer"},
|
||||
peerID: {ID: peerID, FirstName: "Peer"},
|
||||
viaBotID: {ID: viaBotID, FirstName: "Bot", Bot: true},
|
||||
}}}
|
||||
r := New(Config{}, Deps{Users: users}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
list := domain.DialogList{
|
||||
Messages: []domain.Message{{
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: peerID},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: viewerID},
|
||||
ViaBotID: viaBotID,
|
||||
}},
|
||||
Users: []domain.User{
|
||||
{ID: viewerID, FirstName: "Viewer"},
|
||||
{ID: peerID, FirstName: "Peer"},
|
||||
},
|
||||
}
|
||||
|
||||
got := r.withDialogListPresence(ctx, viewerID, list)
|
||||
if users.byIDsCalls != 1 {
|
||||
t.Fatalf("ByIDs calls = %d, want one missing-peer batch", users.byIDsCalls)
|
||||
}
|
||||
if len(users.lastByIDs) != 1 || users.lastByIDs[0] != viaBotID {
|
||||
t.Fatalf("ByIDs ids = %v, want only missing via bot %d", users.lastByIDs, viaBotID)
|
||||
}
|
||||
if len(got.Users) != 3 {
|
||||
t.Fatalf("projected users = %d, want existing two plus missing bot", len(got.Users))
|
||||
}
|
||||
|
||||
users.byIDsCalls = 0
|
||||
users.lastByIDs = nil
|
||||
list.Users = append(list.Users, domain.User{ID: viaBotID, FirstName: "Bot", Bot: true})
|
||||
got = r.withDialogListPresence(ctx, viewerID, list)
|
||||
if users.byIDsCalls != 0 {
|
||||
t.Fatalf("ByIDs calls with complete envelope = %d, want zero", users.byIDsCalls)
|
||||
}
|
||||
if len(got.Users) != 3 {
|
||||
t.Fatalf("complete projected users = %d, want unchanged three", len(got.Users))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue