fix: sync correct collectible username projection
This commit is contained in:
parent
8356989e01
commit
9041abd3d3
42 changed files with 601 additions and 758 deletions
|
|
@ -3,6 +3,7 @@ package rpc
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
|
@ -232,6 +233,101 @@ func usernameStrings(list []tg.Username) []string {
|
|||
return out
|
||||
}
|
||||
|
||||
type tgUsernamePeer interface {
|
||||
SetFlags()
|
||||
GetUsername() (string, bool)
|
||||
GetUsernames() ([]tg.Username, bool)
|
||||
}
|
||||
|
||||
func assertVectorOnlyUsernames(t *testing.T, stage string, peer tgUsernamePeer, want []string) []tg.Username {
|
||||
t.Helper()
|
||||
peer.SetFlags()
|
||||
if scalar, set := peer.GetUsername(); set || scalar != "" {
|
||||
t.Fatalf("%s scalar username = %q (set %v), want absent when usernames vector is present", stage, scalar, set)
|
||||
}
|
||||
vector, set := peer.GetUsernames()
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), want) {
|
||||
t.Fatalf("%s usernames = %v (set %v), want %v", stage, usernameStrings(vector), set, want)
|
||||
}
|
||||
return vector
|
||||
}
|
||||
|
||||
func assertScalarOnlyUsername(t *testing.T, stage string, peer tgUsernamePeer, want string) {
|
||||
t.Helper()
|
||||
peer.SetFlags()
|
||||
if vector, set := peer.GetUsernames(); set || len(vector) != 0 {
|
||||
t.Fatalf("%s usernames = %+v (set %v), want vector absent", stage, vector, set)
|
||||
}
|
||||
if scalar, set := peer.GetUsername(); !set || scalar != want {
|
||||
t.Fatalf("%s scalar username = %q (set %v), want %q", stage, scalar, set, want)
|
||||
}
|
||||
}
|
||||
|
||||
func assertUsernameUpdateEnvelope(t *testing.T, stage string, updates *tg.Updates, want []string) {
|
||||
t.Helper()
|
||||
assertValue := func(valueStage string, value *tg.Updates) {
|
||||
t.Helper()
|
||||
if value == nil || len(value.Updates) != 1 || len(value.Users) != 1 {
|
||||
t.Fatalf("%s envelope = %+v, want one update and one user", valueStage, value)
|
||||
}
|
||||
update, ok := value.Updates[0].(*tg.UpdateUserName)
|
||||
if !ok {
|
||||
t.Fatalf("%s update = %T, want *tg.UpdateUserName", valueStage, value.Updates[0])
|
||||
}
|
||||
if got := usernameStrings(update.Usernames); !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("%s update usernames = %v, want %v", valueStage, got, want)
|
||||
}
|
||||
user, ok := value.Users[0].(*tg.User)
|
||||
if !ok {
|
||||
t.Fatalf("%s companion user = %T, want *tg.User", valueStage, value.Users[0])
|
||||
}
|
||||
assertVectorOnlyUsernames(t, valueStage+" companion", user, want)
|
||||
}
|
||||
assertValue(stage, updates)
|
||||
for _, profile := range []tlprofile.Profile{
|
||||
tlprofile.Profile225,
|
||||
tlprofile.Profile226,
|
||||
tlprofile.Profile227,
|
||||
tlprofile.Profile228,
|
||||
} {
|
||||
profileStage := fmt.Sprintf("%s Layer %d", stage, profile)
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(profile, updates, &wire); err != nil {
|
||||
t.Fatalf("encode %s: %v", profileStage, err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s: %v", profileStage, err)
|
||||
}
|
||||
decodedUpdates, ok := decoded.(*tg.Updates)
|
||||
if !ok {
|
||||
t.Fatalf("decoded %s = %T, want *tg.Updates", profileStage, decoded)
|
||||
}
|
||||
assertValue(profileStage, decodedUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyUsernamesFromRegistryKeepsEditableOnlyPeersScalarOnly(t *testing.T) {
|
||||
user := &tg.User{ID: 11}
|
||||
user.SetUsername("editable_user")
|
||||
channel := &tg.Channel{ID: 22}
|
||||
channel.SetUsername("editable_channel")
|
||||
applyUsernamesFromRegistry(
|
||||
[]tg.UserClass{user},
|
||||
[]tg.ChatClass{channel},
|
||||
map[domain.Peer][]domain.Username{
|
||||
{Type: domain.PeerTypeUser, ID: user.ID}: {
|
||||
{Username: "editable_user", Editable: true, Active: true},
|
||||
},
|
||||
{Type: domain.PeerTypeChannel, ID: channel.ID}: {
|
||||
{Username: "editable_channel", Editable: true, Active: true},
|
||||
},
|
||||
},
|
||||
)
|
||||
assertScalarOnlyUsername(t, "editable-only user", user, "editable_user")
|
||||
assertScalarOnlyUsername(t, "editable-only channel", channel, "editable_channel")
|
||||
}
|
||||
|
||||
func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
||||
registry := newFakeUsernameRegistry()
|
||||
f := newUsernameProjectionFixture(t, registry)
|
||||
|
|
@ -256,16 +352,7 @@ func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
|||
t.Fatalf("users = %d, want 2", len(out))
|
||||
}
|
||||
self := out[0].(*tg.User)
|
||||
if scalar, ok := self.GetUsername(); !ok || scalar != "nft4" {
|
||||
t.Fatalf("self scalar username = %q (set %v), want primary collectible nft4", scalar, ok)
|
||||
}
|
||||
vector, ok := self.GetUsernames()
|
||||
if !ok {
|
||||
t.Fatalf("self usernames unset, want registry vector")
|
||||
}
|
||||
if got := usernameStrings(vector); len(got) != 2 || got[0] != "nft4" || got[1] != "owner_slot" {
|
||||
t.Fatalf("self usernames = %v, want [nft4 owner_slot]", got)
|
||||
}
|
||||
vector := assertVectorOnlyUsernames(t, "self", self, []string{"nft4", "owner_slot"})
|
||||
if vector[0].Editable || !vector[0].Active {
|
||||
t.Fatalf("primary collectible flags = %+v, want non-editable+active", vector[0])
|
||||
}
|
||||
|
|
@ -273,13 +360,7 @@ func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
|||
t.Fatalf("editable slot flags = %+v, want editable+active", vector[1])
|
||||
}
|
||||
friend := out[1].(*tg.User)
|
||||
if scalar, ok := friend.GetUsername(); !ok || scalar != "friend_slot" {
|
||||
t.Fatalf("friend scalar username = %q (set %v), want friend_slot", scalar, ok)
|
||||
}
|
||||
friendVector, _ := friend.GetUsernames()
|
||||
if got := usernameStrings(friendVector); len(got) != 2 || got[1] != "gem4" {
|
||||
t.Fatalf("friend usernames = %v, want [friend_slot gem4]", got)
|
||||
}
|
||||
friendVector := assertVectorOnlyUsernames(t, "friend", friend, []string{"friend_slot", "gem4"})
|
||||
if friendVector[1].Active {
|
||||
t.Fatalf("inactive collectible projected active: %+v", friendVector[1])
|
||||
}
|
||||
|
|
@ -289,7 +370,7 @@ func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResolveUsernamePreservesCompleteUsernamesThroughLayer228(t *testing.T) {
|
||||
func TestResolveUsernamePreservesCompleteUsernamesThroughLayers225To228(t *testing.T) {
|
||||
registry := newFakeUsernameRegistry()
|
||||
f := newUsernameProjectionFixture(t, registry)
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: f.owner.ID}] = []domain.Username{
|
||||
|
|
@ -314,70 +395,62 @@ func TestResolveUsernamePreservesCompleteUsernamesThroughLayer228(t *testing.T)
|
|||
if !ok {
|
||||
t.Fatalf("%s resolved user = %T, want *tg.User", stage, value.Users[0])
|
||||
}
|
||||
if scalar, set := user.GetUsername(); !set || scalar != "owner_slot" {
|
||||
t.Fatalf("%s scalar username = %q (set %v), want owner_slot", stage, scalar, set)
|
||||
}
|
||||
vector, set := user.GetUsernames()
|
||||
want := []string{"owner_slot", "owner_collectible_b", "owner_collectible_a"}
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), want) {
|
||||
t.Fatalf("%s usernames = %v (set %v), want %v", stage, usernameStrings(vector), set, want)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, stage, user, want)
|
||||
}
|
||||
assertResolvedUsernames("canonical", resolved)
|
||||
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(tlprofile.Profile228, resolved, &wire); err != nil {
|
||||
t.Fatalf("encode Layer 228 resolved peer: %v", err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(
|
||||
for _, profile := range []tlprofile.Profile{
|
||||
tlprofile.Profile225,
|
||||
tlprofile.Profile226,
|
||||
tlprofile.Profile227,
|
||||
tlprofile.Profile228,
|
||||
&bin.Buffer{Buf: wire.Copy()},
|
||||
tlprofile.Limits{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("decode Layer 228 resolved peer: %v", err)
|
||||
}
|
||||
decodedResolved, ok := decoded.(*tg.ContactsResolvedPeer)
|
||||
if !ok {
|
||||
t.Fatalf("decoded Layer 228 object = %T, want *tg.ContactsResolvedPeer", decoded)
|
||||
}
|
||||
assertResolvedUsernames("Layer 228", decodedResolved)
|
||||
} {
|
||||
stage := fmt.Sprintf("Layer %d", profile)
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(profile, resolved, &wire); err != nil {
|
||||
t.Fatalf("encode %s resolved peer: %v", stage, err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s resolved peer: %v", stage, err)
|
||||
}
|
||||
decodedResolved, ok := decoded.(*tg.ContactsResolvedPeer)
|
||||
if !ok {
|
||||
t.Fatalf("decoded %s object = %T, want *tg.ContactsResolvedPeer", stage, decoded)
|
||||
}
|
||||
assertResolvedUsernames(stage, decodedResolved)
|
||||
|
||||
requestBody := encodeExactLayerRPC(t, tlprofile.Profile228, &tg.ContactsResolveUsernameRequest{
|
||||
Username: "@owner_slot",
|
||||
})
|
||||
admitted, err := f.router.AdmitLayer(tlprofile.Profile228, &requestBody, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("admit Layer 228 contacts.resolveUsername: %v", err)
|
||||
requestBody := encodeExactLayerRPC(t, profile, &tg.ContactsResolveUsernameRequest{Username: "@owner_slot"})
|
||||
admitted, err := f.router.AdmitLayer(profile, &requestBody, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("admit %s contacts.resolveUsername: %v", stage, err)
|
||||
}
|
||||
result, method, err := f.router.DispatchAdmitted(
|
||||
WithUserID(context.Background(), f.friend.ID),
|
||||
[8]byte{1},
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
admitted,
|
||||
)
|
||||
if err != nil || method != "contacts.resolveUsername" {
|
||||
t.Fatalf("dispatch %s method=%q err=%v", stage, method, err)
|
||||
}
|
||||
var resultWire bin.Buffer
|
||||
if err := result.Encode(&resultWire); err != nil {
|
||||
t.Fatalf("encode %s method result: %v", stage, err)
|
||||
}
|
||||
decodedResult, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: resultWire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s method result: %v", stage, err)
|
||||
}
|
||||
methodResolved, ok := decodedResult.(*tg.ContactsResolvedPeer)
|
||||
if !ok {
|
||||
t.Fatalf("decoded %s method result = %T, want *tg.ContactsResolvedPeer", stage, decodedResult)
|
||||
}
|
||||
assertResolvedUsernames(stage+" method result", methodResolved)
|
||||
}
|
||||
result, method, err := f.router.DispatchAdmitted(
|
||||
WithUserID(context.Background(), f.friend.ID),
|
||||
[8]byte{1},
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
admitted,
|
||||
)
|
||||
if err != nil || method != "contacts.resolveUsername" {
|
||||
t.Fatalf("dispatch Layer 228 method=%q err=%v", method, err)
|
||||
}
|
||||
var resultWire bin.Buffer
|
||||
if err := result.Encode(&resultWire); err != nil {
|
||||
t.Fatalf("encode Layer 228 method result: %v", err)
|
||||
}
|
||||
decodedResult, err := tlprofile.DecodeObject(
|
||||
tlprofile.Profile228,
|
||||
&bin.Buffer{Buf: resultWire.Copy()},
|
||||
tlprofile.Limits{},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("decode Layer 228 method result: %v", err)
|
||||
}
|
||||
methodResolved, ok := decodedResult.(*tg.ContactsResolvedPeer)
|
||||
if !ok {
|
||||
t.Fatalf("decoded Layer 228 method result = %T, want *tg.ContactsResolvedPeer", decodedResult)
|
||||
}
|
||||
assertResolvedUsernames("Layer 228 method result", methodResolved)
|
||||
}
|
||||
|
||||
func TestAuthLoginTokenSuccessProjectsCompleteSelfUsernames(t *testing.T) {
|
||||
|
|
@ -406,29 +479,30 @@ func TestAuthLoginTokenSuccessProjectsCompleteSelfUsernames(t *testing.T) {
|
|||
t.Fatalf("authorization user = %T, want *tg.User", authorization.User)
|
||||
}
|
||||
want := []string{"owner_slot", "owner_collectible_b", "owner_collectible_a"}
|
||||
vector, set := self.GetUsernames()
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), want) {
|
||||
t.Fatalf("authorization usernames = %v (set %v), want %v", usernameStrings(vector), set, want)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "authorization", self, want)
|
||||
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(tlprofile.Profile228, success, &wire); err != nil {
|
||||
t.Fatalf("encode Layer 228 login token success: %v", err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(tlprofile.Profile228, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode Layer 228 login token success: %v", err)
|
||||
}
|
||||
decodedSuccess, ok := decoded.(*tg.AuthLoginTokenSuccess)
|
||||
if !ok {
|
||||
t.Fatalf("decoded login token success = %T", decoded)
|
||||
}
|
||||
decodedAuthorization := decodedSuccess.Authorization.(*tg.AuthAuthorization)
|
||||
decodedSelf := decodedAuthorization.User.(*tg.User)
|
||||
decodedVector, decodedSet := decodedSelf.GetUsernames()
|
||||
if !decodedSet || !reflect.DeepEqual(usernameStrings(decodedVector), want) {
|
||||
t.Fatalf("decoded authorization usernames = %v (set %v), want %v",
|
||||
usernameStrings(decodedVector), decodedSet, want)
|
||||
for _, profile := range []tlprofile.Profile{
|
||||
tlprofile.Profile225,
|
||||
tlprofile.Profile226,
|
||||
tlprofile.Profile227,
|
||||
tlprofile.Profile228,
|
||||
} {
|
||||
stage := fmt.Sprintf("Layer %d authorization", profile)
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(profile, success, &wire); err != nil {
|
||||
t.Fatalf("encode %s: %v", stage, err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s: %v", stage, err)
|
||||
}
|
||||
decodedSuccess, ok := decoded.(*tg.AuthLoginTokenSuccess)
|
||||
if !ok {
|
||||
t.Fatalf("decoded %s = %T", stage, decoded)
|
||||
}
|
||||
decodedAuthorization := decodedSuccess.Authorization.(*tg.AuthAuthorization)
|
||||
decodedSelf := decodedAuthorization.User.(*tg.User)
|
||||
assertVectorOnlyUsernames(t, stage, decodedSelf, want)
|
||||
}
|
||||
if registry.peerCalls != 1 || registry.batchCalls != 0 {
|
||||
t.Fatalf("authorization username reads = peer:%d batch:%d, want 1/0",
|
||||
|
|
@ -436,6 +510,122 @@ func TestAuthLoginTokenSuccessProjectsCompleteSelfUsernames(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAccountProfileMutationResponsesKeepVectorOnlyUsernames(t *testing.T) {
|
||||
registry := newFakeUsernameRegistry()
|
||||
f := newUsernameProjectionFixture(t, registry)
|
||||
sessions := &captureSessions{}
|
||||
f.router.deps.Sessions = sessions
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: f.owner.ID}
|
||||
registry.byPeer[peer] = []domain.Username{
|
||||
{Username: "owner_slot", Editable: true, Active: true, SortOrder: 0},
|
||||
{Username: "owner_collectible", Active: true, SortOrder: 1, CollectibleID: 21},
|
||||
}
|
||||
ctx := WithUserID(context.Background(), f.owner.ID)
|
||||
profile := &tg.AccountUpdateProfileRequest{}
|
||||
profile.SetFirstName("Updated")
|
||||
|
||||
updated, err := f.router.onAccountUpdateProfile(ctx, profile)
|
||||
if err != nil {
|
||||
t.Fatalf("account.updateProfile: %v", err)
|
||||
}
|
||||
self, ok := updated.(*tg.User)
|
||||
if !ok {
|
||||
t.Fatalf("account.updateProfile user = %T, want *tg.User", updated)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "account.updateProfile", self, []string{"owner_slot", "owner_collectible"})
|
||||
profileEnvelope := sessions.lastUserPush().(*tg.Updates)
|
||||
pushed := profileEnvelope.Users[0].(*tg.User)
|
||||
if pushed == self {
|
||||
t.Fatal("account.updateProfile reused one mutable tg.User for RPC result and push")
|
||||
}
|
||||
assertUsernameUpdateEnvelope(t, "account.updateProfile push", profileEnvelope, []string{"owner_slot", "owner_collectible"})
|
||||
if registry.peerCalls != 1 || registry.batchCalls != 0 {
|
||||
t.Fatalf("account.updateProfile username reads = peer:%d batch:%d, want 1/0", registry.peerCalls, registry.batchCalls)
|
||||
}
|
||||
|
||||
registry.byPeer[peer] = []domain.Username{
|
||||
{Username: "renamed_slot", Editable: true, Active: true, SortOrder: 0},
|
||||
{Username: "owner_collectible", Active: true, SortOrder: 1, CollectibleID: 21},
|
||||
}
|
||||
updated, err = f.router.onAccountUpdateUsername(ctx, "renamed_slot")
|
||||
if err != nil {
|
||||
t.Fatalf("account.updateUsername: %v", err)
|
||||
}
|
||||
self, ok = updated.(*tg.User)
|
||||
if !ok {
|
||||
t.Fatalf("account.updateUsername user = %T, want *tg.User", updated)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "account.updateUsername", self, []string{"renamed_slot", "owner_collectible"})
|
||||
usernameEnvelope := sessions.lastUserPush().(*tg.Updates)
|
||||
pushed = usernameEnvelope.Users[0].(*tg.User)
|
||||
if pushed == self {
|
||||
t.Fatal("account.updateUsername reused one mutable tg.User for RPC result and push")
|
||||
}
|
||||
assertUsernameUpdateEnvelope(t, "account.updateUsername push", usernameEnvelope, []string{"renamed_slot", "owner_collectible"})
|
||||
if registry.peerCalls != 2 || registry.batchCalls != 0 {
|
||||
t.Fatalf("profile mutation username reads = peer:%d batch:%d, want 2/0", registry.peerCalls, registry.batchCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPremiumStatusProjectionSkipsOfflineUsernameRead(t *testing.T) {
|
||||
const userID = int64(1000000888)
|
||||
registry := newFakeUsernameRegistry()
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: userID}] = []domain.Username{
|
||||
{Username: "premium_slot", Editable: true, Active: true, SortOrder: 0},
|
||||
{Username: "premium_collectible", Active: true, SortOrder: 1, CollectibleID: 88},
|
||||
}
|
||||
sessions := &captureSessions{}
|
||||
r := New(Config{}, Deps{Sessions: sessions, Usernames: registry}, zaptest.NewLogger(t), clock.System)
|
||||
u := domain.User{ID: userID, FirstName: "Premium", Username: "premium_slot"}
|
||||
|
||||
r.pushPremiumStatusUpdate(context.Background(), u)
|
||||
if registry.peerCalls != 0 || sessions.lastUserPush() != nil {
|
||||
t.Fatalf("offline premium push = registry reads:%d message:%T, want no work", registry.peerCalls, sessions.lastUserPush())
|
||||
}
|
||||
|
||||
sessions.onlineUserIDs = []int64{userID}
|
||||
r.pushPremiumStatusUpdate(context.Background(), u)
|
||||
if registry.peerCalls != 1 {
|
||||
t.Fatalf("online premium username reads = %d, want 1", registry.peerCalls)
|
||||
}
|
||||
updates, ok := sessions.lastUserPush().(*tg.Updates)
|
||||
if !ok || len(updates.Users) != 1 {
|
||||
t.Fatalf("online premium push = %T %+v", sessions.lastUserPush(), sessions.lastUserPush())
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "premium status update", updates.Users[0].(*tg.User), []string{"premium_slot", "premium_collectible"})
|
||||
}
|
||||
|
||||
func TestPremiumStatusProjectionBatchesOnlineUsers(t *testing.T) {
|
||||
registry := newFakeUsernameRegistry()
|
||||
users := []domain.User{
|
||||
{ID: 101, FirstName: "One", Username: "one_slot"},
|
||||
{ID: 102, FirstName: "Two", Username: "two_slot"},
|
||||
{ID: 103, FirstName: "Offline", Username: "offline_slot"},
|
||||
}
|
||||
for _, u := range users {
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: u.ID}] = []domain.Username{
|
||||
{Username: u.Username, Editable: true, Active: true, SortOrder: 0},
|
||||
{Username: u.Username + "_collectible", Active: true, SortOrder: 1, CollectibleID: u.ID},
|
||||
}
|
||||
}
|
||||
sessions := &captureSessions{onlineUserIDs: []int64{101, 102}}
|
||||
r := New(Config{}, Deps{Sessions: sessions, Usernames: registry}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
r.pushPremiumStatusUpdates(context.Background(), []domain.User{users[0], users[1], users[0], users[2]})
|
||||
|
||||
if registry.batchCalls != 1 || registry.peerCalls != 0 {
|
||||
t.Fatalf("premium batch username reads = batch:%d peer:%d, want 1/0", registry.batchCalls, registry.peerCalls)
|
||||
}
|
||||
if got, want := sessions.pushedUserIDs(), []int64{101, 102}; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("premium batch pushed users = %v, want %v", got, want)
|
||||
}
|
||||
updates, ok := sessions.lastUserPush().(*tg.Updates)
|
||||
if !ok || len(updates.Users) != 1 {
|
||||
t.Fatalf("premium batch last push = %T %+v", sessions.lastUserPush(), sessions.lastUserPush())
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "premium batch last user", updates.Users[0].(*tg.User), []string{"two_slot", "two_slot_collectible"})
|
||||
}
|
||||
|
||||
func TestMessageEchoProjectsCompleteUsernamesInOneBatch(t *testing.T) {
|
||||
registry := newFakeUsernameRegistry()
|
||||
f := newUsernameProjectionFixture(t, registry)
|
||||
|
|
@ -465,10 +655,7 @@ func TestMessageEchoProjectsCompleteUsernamesInOneBatch(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatalf("message echo user = %T, want *tg.User", item)
|
||||
}
|
||||
vector, set := user.GetUsernames()
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), want[user.ID]) {
|
||||
t.Fatalf("user %d usernames = %v (set %v), want %v", user.ID, usernameStrings(vector), set, want[user.ID])
|
||||
}
|
||||
assertVectorOnlyUsernames(t, fmt.Sprintf("message echo user %d", user.ID), user, want[user.ID])
|
||||
}
|
||||
if registry.batchCalls != 1 || registry.peerCalls != 0 {
|
||||
t.Fatalf("registry reads = batch %d / peer %d, want one batch read for the response", registry.batchCalls, registry.peerCalls)
|
||||
|
|
@ -516,10 +703,7 @@ func TestChannelMessageUpdatesProjectCompleteUsernames(t *testing.T) {
|
|||
t.Fatalf("channel updates users = %+v, want one sender", updates)
|
||||
}
|
||||
user := updates.Users[0].(*tg.User)
|
||||
vector, set := user.GetUsernames()
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), []string{"channel_sender", "channel_collectible"}) {
|
||||
t.Fatalf("channel sender usernames = %v (set %v), want complete vector", usernameStrings(vector), set)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "channel sender", user, []string{"channel_sender", "channel_collectible"})
|
||||
if registry.peerCalls != 0 || registry.batchCalls != 1 {
|
||||
t.Fatalf("registry reads = peer %d / batch %d, want one batched user+channel read", registry.peerCalls, registry.batchCalls)
|
||||
}
|
||||
|
|
@ -561,10 +745,7 @@ func TestChannelDifferenceProjectsCompleteUsernames(t *testing.T) {
|
|||
t.Fatalf("channel difference = %T %+v, want one user", out, out)
|
||||
}
|
||||
user := diff.Users[0].(*tg.User)
|
||||
vector, set := user.GetUsernames()
|
||||
if !set || !reflect.DeepEqual(usernameStrings(vector), []string{"difference_sender", "difference_collectible"}) {
|
||||
t.Fatalf("channel difference usernames = %v (set %v), want complete vector", usernameStrings(vector), set)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, "channel difference sender", user, []string{"difference_sender", "difference_collectible"})
|
||||
if registry.batchCalls != 1 || registry.peerCalls != 0 {
|
||||
t.Fatalf("registry reads = batch %d / peer %d, want one batched user+channel read", registry.batchCalls, registry.peerCalls)
|
||||
}
|
||||
|
|
@ -580,14 +761,7 @@ func TestUsersGetUsersDegradesWithoutRegistry(t *testing.T) {
|
|||
}
|
||||
self := out[0].(*tg.User)
|
||||
// Without a collectible registry the official legacy shape is scalar-only.
|
||||
self.SetFlags()
|
||||
vector, ok := self.GetUsernames()
|
||||
if ok || len(vector) != 0 {
|
||||
t.Fatalf("usernames = %+v (set %v), want vector absent", vector, ok)
|
||||
}
|
||||
if scalar, ok := self.GetUsername(); !ok || scalar != "owner_slot" {
|
||||
t.Fatalf("scalar username = %q (set %v), want owner_slot", scalar, ok)
|
||||
}
|
||||
assertScalarOnlyUsername(t, "self without registry", self, "owner_slot")
|
||||
}
|
||||
|
||||
func TestUsersGetUsersDegradesWhenRegistryFails(t *testing.T) {
|
||||
|
|
@ -608,14 +782,7 @@ func TestUsersGetUsersDegradesWhenRegistryFails(t *testing.T) {
|
|||
}
|
||||
for i, want := range []string{"owner_slot", "friend_slot"} {
|
||||
user := out[i].(*tg.User)
|
||||
user.SetFlags()
|
||||
vector, ok := user.GetUsernames()
|
||||
if ok || len(vector) != 0 {
|
||||
t.Fatalf("users[%d] usernames = %+v, want vector absent", i, vector)
|
||||
}
|
||||
if scalar, ok := user.GetUsername(); !ok || scalar != want {
|
||||
t.Fatalf("users[%d] scalar username = %q (set %v), want %q", i, scalar, ok, want)
|
||||
}
|
||||
assertScalarOnlyUsername(t, fmt.Sprintf("users[%d] with failed registry", i), user, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -963,15 +1130,27 @@ func TestChannelsGetChannelsProjectsCollectibleUsernames(t *testing.T) {
|
|||
if len(out) != 1 {
|
||||
t.Fatalf("chats = %d, want 1", len(out))
|
||||
}
|
||||
vector, ok := out[0].(*tg.Channel).GetUsernames()
|
||||
if !ok {
|
||||
t.Fatalf("channel usernames unset, want registry vector")
|
||||
}
|
||||
if got := usernameStrings(vector); len(got) != 2 || got[0] != "chan_nft" || got[1] != "chan_slot" {
|
||||
t.Fatalf("channel usernames = %v, want [chan_nft chan_slot]", got)
|
||||
}
|
||||
if scalar, ok := out[0].(*tg.Channel).GetUsername(); !ok || scalar != "chan_nft" {
|
||||
t.Fatalf("scalar channel username = %q (set %v), want primary collectible chan_nft", scalar, ok)
|
||||
assertVectorOnlyUsernames(t, "channel", out[0].(*tg.Channel), []string{"chan_nft", "chan_slot"})
|
||||
for _, profile := range []tlprofile.Profile{
|
||||
tlprofile.Profile225,
|
||||
tlprofile.Profile226,
|
||||
tlprofile.Profile227,
|
||||
tlprofile.Profile228,
|
||||
} {
|
||||
stage := fmt.Sprintf("Layer %d channel", profile)
|
||||
var wire bin.Buffer
|
||||
if err := tlprofile.EncodeObject(profile, chats, &wire); err != nil {
|
||||
t.Fatalf("encode %s: %v", stage, err)
|
||||
}
|
||||
decoded, err := tlprofile.DecodeObject(profile, &bin.Buffer{Buf: wire.Copy()}, tlprofile.Limits{})
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s: %v", stage, err)
|
||||
}
|
||||
decodedChats, ok := decoded.(*tg.MessagesChats)
|
||||
if !ok || len(decodedChats.Chats) != 1 {
|
||||
t.Fatalf("decoded %s = %T %+v", stage, decoded, decoded)
|
||||
}
|
||||
assertVectorOnlyUsernames(t, stage, decodedChats.Chats[0].(*tg.Channel), []string{"chan_nft", "chan_slot"})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -985,11 +1164,5 @@ func TestChannelsGetChannelsDegradesWithoutRegistry(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("get channels: %v", err)
|
||||
}
|
||||
vector, ok := chats.(*tg.MessagesChats).Chats[0].(*tg.Channel).GetUsernames()
|
||||
if ok || len(vector) != 0 {
|
||||
t.Fatalf("legacy channel usernames = %+v (set %v), want vector absent", vector, ok)
|
||||
}
|
||||
if scalar, ok := chats.(*tg.MessagesChats).Chats[0].(*tg.Channel).GetUsername(); !ok || scalar != "chan_slot" {
|
||||
t.Fatalf("legacy scalar username = %q (set %v), want chan_slot", scalar, ok)
|
||||
}
|
||||
assertScalarOnlyUsername(t, "channel without registry", chats.(*tg.MessagesChats).Chats[0].(*tg.Channel), "chan_slot")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue