fix(usernames): sync index active collectible aliases
This commit is contained in:
parent
2f995f607a
commit
5156b17c1b
29 changed files with 657 additions and 112 deletions
|
|
@ -234,12 +234,12 @@ func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
|||
registry := newFakeUsernameRegistry()
|
||||
f := newUsernameProjectionFixture(t, registry)
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: f.owner.ID}] = []domain.Username{
|
||||
{Username: "owner_slot", Editable: true, Active: true},
|
||||
{Username: "nft", Active: true, SortOrder: 0, CollectibleID: 7},
|
||||
{Username: "owner_slot", Editable: true, Active: true, SortOrder: 1},
|
||||
{Username: "nft4", Active: true, SortOrder: 0, CollectibleID: 7},
|
||||
}
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: f.friend.ID}] = []domain.Username{
|
||||
{Username: "friend_slot", Editable: true, Active: true},
|
||||
{Username: "gem", Active: false, SortOrder: 0, CollectibleID: 8},
|
||||
{Username: "gem4", Active: false, SortOrder: 1, CollectibleID: 8},
|
||||
}
|
||||
ctx := WithUserID(context.Background(), f.owner.ID)
|
||||
|
||||
|
|
@ -254,26 +254,29 @@ func TestUsersGetUsersProjectsCollectibleUsernamesInOneBatch(t *testing.T) {
|
|||
t.Fatalf("users = %d, want 2", len(out))
|
||||
}
|
||||
self := out[0].(*tg.User)
|
||||
if _, ok := self.GetUsername(); ok {
|
||||
t.Fatalf("self scalar username is set together with collectible vector")
|
||||
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] != "owner_slot" || got[1] != "nft" {
|
||||
t.Fatalf("self usernames = %v, want [owner_slot nft]", got)
|
||||
if got := usernameStrings(vector); len(got) != 2 || got[0] != "nft4" || got[1] != "owner_slot" {
|
||||
t.Fatalf("self usernames = %v, want [nft4 owner_slot]", got)
|
||||
}
|
||||
if !vector[0].Editable || !vector[0].Active {
|
||||
t.Fatalf("editable slot flags = %+v, want editable+active", vector[0])
|
||||
if vector[0].Editable || !vector[0].Active {
|
||||
t.Fatalf("primary collectible flags = %+v, want non-editable+active", vector[0])
|
||||
}
|
||||
if vector[1].Editable || !vector[1].Active {
|
||||
t.Fatalf("collectible flags = %+v, want non-editable+active", vector[1])
|
||||
if !vector[1].Editable || !vector[1].Active {
|
||||
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] != "gem" {
|
||||
t.Fatalf("friend usernames = %v, want [friend_slot gem]", got)
|
||||
if got := usernameStrings(friendVector); len(got) != 2 || got[1] != "gem4" {
|
||||
t.Fatalf("friend usernames = %v, want [friend_slot gem4]", got)
|
||||
}
|
||||
if friendVector[1].Active {
|
||||
t.Fatalf("inactive collectible projected active: %+v", friendVector[1])
|
||||
|
|
@ -663,7 +666,7 @@ func TestChannelsGetChannelsProjectsCollectibleUsernames(t *testing.T) {
|
|||
r, owner, channel := newCollectibleChannelFixture(t, registry)
|
||||
ctx := WithUserID(context.Background(), owner.ID)
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeChannel, ID: channel.ID}] = []domain.Username{
|
||||
{Username: "chan_slot", Editable: true, Active: true},
|
||||
{Username: "chan_slot", Editable: true, Active: true, SortOrder: 1},
|
||||
{Username: "chan_nft", Active: true, SortOrder: 0, CollectibleID: 44},
|
||||
}
|
||||
|
||||
|
|
@ -681,11 +684,11 @@ func TestChannelsGetChannelsProjectsCollectibleUsernames(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatalf("channel usernames unset, want registry vector")
|
||||
}
|
||||
if got := usernameStrings(vector); len(got) != 2 || got[0] != "chan_slot" || got[1] != "chan_nft" {
|
||||
t.Fatalf("channel usernames = %v, want [chan_slot chan_nft]", got)
|
||||
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 != "" {
|
||||
t.Fatalf("scalar channel username = %q (set %v), want absent with collectible vector", scalar, ok)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
appprivacy "telesrv/internal/app/privacy"
|
||||
appstories "telesrv/internal/app/stories"
|
||||
appupdates "telesrv/internal/app/updates"
|
||||
usernamesapp "telesrv/internal/app/usernames"
|
||||
"telesrv/internal/app/userprojection"
|
||||
appusers "telesrv/internal/app/users"
|
||||
"telesrv/internal/domain"
|
||||
|
|
@ -24,6 +25,8 @@ import (
|
|||
func TestContactsSearchFindsUsers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
users := memory.NewUserStore()
|
||||
registry := memory.NewCollectibleUsernameStore()
|
||||
users.AttachUsernameRegistry(registry)
|
||||
owner, err := users.Create(ctx, domain.User{AccessHash: 1, Phone: "15550000001", FirstName: "Owner"})
|
||||
if err != nil {
|
||||
t.Fatalf("create owner: %v", err)
|
||||
|
|
@ -32,12 +35,29 @@ func TestContactsSearchFindsUsers(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("create friend: %v", err)
|
||||
}
|
||||
friendPeer := domain.Peer{Type: domain.PeerTypeUser, ID: friend.ID}
|
||||
if _, err := registry.SetEditableUsername(ctx, friendPeer, friend.Username); err != nil {
|
||||
t.Fatalf("seed editable username: %v", err)
|
||||
}
|
||||
if _, created, err := registry.MintCollectibleUsername(ctx, domain.MintCollectibleUsernameRequest{
|
||||
Username: "nft4",
|
||||
Owner: friendPeer,
|
||||
Currency: domain.CollectibleCurrencyStars,
|
||||
Amount: 1,
|
||||
Actor: "test",
|
||||
}); err != nil || !created {
|
||||
t.Fatalf("mint collectible: created=%v err=%v", created, err)
|
||||
}
|
||||
r := New(Config{}, Deps{
|
||||
Contacts: appcontacts.NewService(memory.NewContactStore(), users),
|
||||
Usernames: usernamesapp.NewService(
|
||||
usernamesapp.WithRegistryStore(registry),
|
||||
usernamesapp.WithCollectibleStore(registry),
|
||||
),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
var in bin.Buffer
|
||||
if err := (&tg.ContactsSearchRequest{Q: "@search", Limit: 20}).Encode(&in); err != nil {
|
||||
if err := (&tg.ContactsSearchRequest{Q: "@NFT4", Limit: 20}).Encode(&in); err != nil {
|
||||
t.Fatalf("encode request: %v", err)
|
||||
}
|
||||
enc, err := r.Dispatch(WithUserID(ctx, owner.ID), [8]byte{}, 0, &in)
|
||||
|
|
@ -55,6 +75,14 @@ func TestContactsSearchFindsUsers(t *testing.T) {
|
|||
if !ok || peer.UserID != friend.ID {
|
||||
t.Fatalf("peer = %T %+v, want friend", box.Results[0], box.Results[0])
|
||||
}
|
||||
user := box.Users[0].(*tg.User)
|
||||
if scalar, ok := user.GetUsername(); !ok || scalar != "search_friend" {
|
||||
t.Fatalf("search result scalar username = %q (set %v), want search_friend", scalar, ok)
|
||||
}
|
||||
vector, ok := user.GetUsernames()
|
||||
if !ok || len(vector) != 2 || vector[1].Username != "nft4" || !vector[1].Active {
|
||||
t.Fatalf("search result username vector = %+v (set %v), want active nft4 alias", vector, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContactsEditCloseFriendsProjectsUserFlag(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -302,8 +302,16 @@ func applyUsernamesFromRegistry(users []tg.UserClass, chats []tg.ChatClass, byPe
|
|||
continue
|
||||
}
|
||||
if vector := tgUsernamesFromRegistry(list, u.Username); len(vector) > 0 {
|
||||
u.Flags.Unset(3)
|
||||
u.Username = ""
|
||||
// Layer 228 defines username as the main active username, not as a
|
||||
// legacy alternative to usernames. TDesktop seeds its local search
|
||||
// index from this scalar before consuming the complete vector, so
|
||||
// both fields must be projected together.
|
||||
if primary := domain.ActiveUsername(list); primary != "" {
|
||||
u.SetUsername(primary)
|
||||
} else {
|
||||
u.Flags.Unset(3)
|
||||
u.Username = ""
|
||||
}
|
||||
u.SetUsernames(vector)
|
||||
}
|
||||
}
|
||||
|
|
@ -320,8 +328,12 @@ func applyUsernamesFromRegistry(users []tg.UserClass, chats []tg.ChatClass, byPe
|
|||
// when unset, which is exactly the fallback tgUsernamesFromRegistry wants.
|
||||
scalar, _ := ch.GetUsername()
|
||||
if vector := tgUsernamesFromRegistry(list, scalar); len(vector) > 0 {
|
||||
ch.Flags.Unset(6)
|
||||
ch.Username = ""
|
||||
if primary := domain.ActiveUsername(list); primary != "" {
|
||||
ch.SetUsername(primary)
|
||||
} else {
|
||||
ch.Flags.Unset(6)
|
||||
ch.Username = ""
|
||||
}
|
||||
ch.SetUsernames(vector)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ func TestNotifyPeerUsernamesChangedUserPushesPreloadedVector(t *testing.T) {
|
|||
}
|
||||
registry := newFakeUsernameRegistry()
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeUser, ID: targetID}] = []domain.Username{
|
||||
{Username: "owner_slot", Editable: true, Active: true},
|
||||
{Username: "nft", Active: true, CollectibleID: 7},
|
||||
{Username: "owner_slot", Editable: true, Active: true, SortOrder: 1},
|
||||
{Username: "nft4", Active: true, SortOrder: 0, CollectibleID: 7},
|
||||
}
|
||||
sessions := &captureSessions{onlineUserIDs: []int64{targetID, viewerID}}
|
||||
r := New(Config{}, Deps{Users: users, Usernames: registry, Sessions: sessions}, zap.NewNop(), clock.System)
|
||||
|
|
@ -42,11 +42,11 @@ func TestNotifyPeerUsernamesChangedUserPushesPreloadedVector(t *testing.T) {
|
|||
}
|
||||
updates := sessions.lastUserPush().(*tg.Updates)
|
||||
user := updates.Users[0].(*tg.User)
|
||||
if scalar, ok := user.GetUsername(); ok || scalar != "" {
|
||||
t.Fatalf("pushed scalar username = %q (set %v), want absent", scalar, ok)
|
||||
if scalar, ok := user.GetUsername(); !ok || scalar != "nft4" {
|
||||
t.Fatalf("pushed scalar username = %q (set %v), want primary collectible nft4", scalar, ok)
|
||||
}
|
||||
vector, ok := user.GetUsernames()
|
||||
if !ok || len(vector) != 2 || vector[1].Username != "nft" {
|
||||
if !ok || len(vector) != 2 || vector[0].Username != "nft4" {
|
||||
t.Fatalf("pushed username vector = %+v (set %v)", vector, ok)
|
||||
}
|
||||
}
|
||||
|
|
@ -63,8 +63,8 @@ func TestNotifyPeerUsernamesChangedChannelPushesPreloadedVector(t *testing.T) {
|
|||
}}
|
||||
registry := newFakeUsernameRegistry()
|
||||
registry.byPeer[domain.Peer{Type: domain.PeerTypeChannel, ID: channelID}] = []domain.Username{
|
||||
{Username: "channel_slot", Editable: true, Active: true},
|
||||
{Username: "collectible", Active: true, CollectibleID: 9},
|
||||
{Username: "channel_slot", Editable: true, Active: true, SortOrder: 1},
|
||||
{Username: "collectible", Active: true, SortOrder: 0, CollectibleID: 9},
|
||||
}
|
||||
sessions := &captureSessions{
|
||||
onlineUserIDs: []int64{ownerID, memberID},
|
||||
|
|
@ -84,11 +84,11 @@ func TestNotifyPeerUsernamesChangedChannelPushesPreloadedVector(t *testing.T) {
|
|||
}
|
||||
updates := sessions.lastUserPush().(*tg.Updates)
|
||||
channel := updates.Chats[0].(*tg.Channel)
|
||||
if scalar, ok := channel.GetUsername(); ok || scalar != "" {
|
||||
t.Fatalf("pushed scalar username = %q (set %v), want absent", scalar, ok)
|
||||
if scalar, ok := channel.GetUsername(); !ok || scalar != "collectible" {
|
||||
t.Fatalf("pushed scalar username = %q (set %v), want primary collectible", scalar, ok)
|
||||
}
|
||||
vector, ok := channel.GetUsernames()
|
||||
if !ok || len(vector) != 2 || vector[1].Username != "collectible" {
|
||||
if !ok || len(vector) != 2 || vector[0].Username != "collectible" {
|
||||
t.Fatalf("pushed username vector = %+v (set %v)", vector, ok)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue