feat: sync chatlist sharing support
This commit is contained in:
parent
ec6e8fd13d
commit
2a9c12263f
62 changed files with 3408 additions and 212 deletions
488
internal/app/chatlists/service_test.go
Normal file
488
internal/app/chatlists/service_test.go
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
package chatlists
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/links"
|
||||
"telesrv/internal/store/memory"
|
||||
)
|
||||
|
||||
func TestSharedFolderInviteJoinUpdatesAndLeave(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
chatlists := memory.NewChatlistStore()
|
||||
svc := NewService(chatlists, dialogs, WithSlugGenerator(func() (string, error) { return "slug-one", nil }))
|
||||
|
||||
ownerID := int64(1001)
|
||||
viewerID := int64(2002)
|
||||
peerA := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
peerB := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3002}, AccessHash: 32}
|
||||
peerC := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3003}, AccessHash: 33}
|
||||
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IncludePeers: []domain.DialogFolderPeer{peerA},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed owner folder: %v", err)
|
||||
}
|
||||
|
||||
folder, invite, err := svc.ExportInvite(ctx, ownerID, 2, "Main link", []domain.DialogFolderPeer{peerA}, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("ExportInvite: %v", err)
|
||||
}
|
||||
if invite.Slug != "slug-one" || !folder.IsChatlist || !folder.HasMyInvites {
|
||||
t.Fatalf("export = folder %+v invite %+v, want chatlist with invite slug", folder, invite)
|
||||
}
|
||||
persisted, found, err := dialogs.GetFolder(ctx, ownerID, 2)
|
||||
if err != nil || !found || !persisted.IsChatlist || !persisted.HasMyInvites {
|
||||
t.Fatalf("persisted folder = %+v found %v err %v, want exported chatlist", persisted, found, err)
|
||||
}
|
||||
|
||||
preview, err := svc.CheckInvite(ctx, viewerID, "https://t.me/addlist/slug-one")
|
||||
if err != nil {
|
||||
t.Fatalf("CheckInvite before join: %v", err)
|
||||
}
|
||||
if len(preview.Missing) != 1 || preview.Missing[0].Peer != peerA.Peer || preview.LocalFolder != nil {
|
||||
t.Fatalf("preview before join = %+v, want missing peerA and no local folder", preview)
|
||||
}
|
||||
|
||||
joined, err := svc.JoinInvite(ctx, viewerID, "slug-one", nil, 101)
|
||||
if err != nil {
|
||||
t.Fatalf("JoinInvite: %v", err)
|
||||
}
|
||||
if joined.Folder.ID != 2 || !joined.Folder.IsChatlist || joined.Folder.HasMyInvites || len(joined.Folder.IncludePeers) != 1 {
|
||||
t.Fatalf("joined folder = %+v, want imported local chatlist with peerA", joined.Folder)
|
||||
}
|
||||
|
||||
preview, err = svc.CheckInvite(ctx, viewerID, "slug-one")
|
||||
if err != nil {
|
||||
t.Fatalf("CheckInvite after join: %v", err)
|
||||
}
|
||||
if preview.LocalFolder == nil || preview.Membership == nil || len(preview.Already) != 1 || len(preview.Missing) != 0 {
|
||||
t.Fatalf("preview after join = %+v, want already imported", preview)
|
||||
}
|
||||
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IsChatlist: true,
|
||||
HasMyInvites: true,
|
||||
IncludePeers: []domain.DialogFolderPeer{peerA, peerB},
|
||||
}); err != nil {
|
||||
t.Fatalf("extend owner folder: %v", err)
|
||||
}
|
||||
if _, err := svc.EditInvite(ctx, ownerID, 2, "slug-one", nil, &[]domain.DialogFolderPeer{peerA, peerB}, false); err != nil {
|
||||
t.Fatalf("EditInvite add peerB: %v", err)
|
||||
}
|
||||
updates, err := svc.GetUpdates(ctx, viewerID, joined.Folder.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUpdates: %v", err)
|
||||
}
|
||||
if len(updates.Missing) != 1 || updates.Missing[0].Peer != peerB.Peer {
|
||||
t.Fatalf("updates = %+v, want missing peerB", updates)
|
||||
}
|
||||
ownerUpdates, err := svc.GetUpdates(ctx, ownerID, folder.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUpdates owner exported folder: %v", err)
|
||||
}
|
||||
if len(ownerUpdates.Missing) != 0 {
|
||||
t.Fatalf("owner updates = %+v, want empty", ownerUpdates)
|
||||
}
|
||||
updated, err := svc.JoinUpdates(ctx, viewerID, joined.Folder.ID, []domain.DialogFolderPeer{peerB}, 102)
|
||||
if err != nil {
|
||||
t.Fatalf("JoinUpdates: %v", err)
|
||||
}
|
||||
if len(updated.Folder.IncludePeers) != 2 {
|
||||
t.Fatalf("updated folder peers = %+v, want 2 peers", updated.Folder.IncludePeers)
|
||||
}
|
||||
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IsChatlist: true,
|
||||
HasMyInvites: true,
|
||||
IncludePeers: []domain.DialogFolderPeer{peerA, peerB, peerC},
|
||||
}); err != nil {
|
||||
t.Fatalf("extend owner folder with peerC: %v", err)
|
||||
}
|
||||
if _, err := svc.EditInvite(ctx, ownerID, 2, "slug-one", nil, &[]domain.DialogFolderPeer{peerA, peerB, peerC}, false); err != nil {
|
||||
t.Fatalf("EditInvite add peerC: %v", err)
|
||||
}
|
||||
if err := svc.HideUpdates(ctx, viewerID, joined.Folder.ID); err != nil {
|
||||
t.Fatalf("HideUpdates: %v", err)
|
||||
}
|
||||
updates, err = svc.GetUpdates(ctx, viewerID, joined.Folder.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUpdates after hide: %v", err)
|
||||
}
|
||||
if len(updates.Missing) != 0 {
|
||||
t.Fatalf("updates after hide = %+v, want empty", updates)
|
||||
}
|
||||
|
||||
suggestions, err := svc.LeaveSuggestions(ctx, viewerID, joined.Folder.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("LeaveSuggestions: %v", err)
|
||||
}
|
||||
if len(suggestions) != 2 {
|
||||
t.Fatalf("leave suggestions = %+v, want current two imported peers", suggestions)
|
||||
}
|
||||
if _, err := svc.Leave(ctx, viewerID, joined.Folder.ID, suggestions, 103); err != nil {
|
||||
t.Fatalf("Leave: %v", err)
|
||||
}
|
||||
if _, found, err := dialogs.GetFolder(ctx, viewerID, joined.Folder.ID); err != nil || found {
|
||||
t.Fatalf("local folder after leave found=%v err=%v, want deleted", found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteLastSharedFolderInviteClearsOwnerFlag(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
chatlists := memory.NewChatlistStore()
|
||||
slugs := []string{"slug-a", "slug-b"}
|
||||
svc := NewService(chatlists, dialogs, WithSlugGenerator(func() (string, error) {
|
||||
slug := slugs[0]
|
||||
slugs = slugs[1:]
|
||||
return slug, nil
|
||||
}))
|
||||
|
||||
ownerID := int64(1001)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed owner folder: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "one", []domain.DialogFolderPeer{peer}, 100); err != nil {
|
||||
t.Fatalf("ExportInvite one: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "two", []domain.DialogFolderPeer{peer}, 101); err != nil {
|
||||
t.Fatalf("ExportInvite two: %v", err)
|
||||
}
|
||||
if folder, changed, err := svc.DeleteInvite(ctx, ownerID, 2, "slug-a"); err != nil || changed || folder.HasMyInvites {
|
||||
t.Fatalf("DeleteInvite first = folder %+v changed %v err %v, want no folder update", folder, changed, err)
|
||||
}
|
||||
persisted, found, err := dialogs.GetFolder(ctx, ownerID, 2)
|
||||
if err != nil || !found || !persisted.HasMyInvites {
|
||||
t.Fatalf("folder after first delete = %+v found %v err %v, want has_my_invites", persisted, found, err)
|
||||
}
|
||||
folder, changed, err := svc.DeleteInvite(ctx, ownerID, 2, "slug-b")
|
||||
if err != nil || !changed || folder.HasMyInvites {
|
||||
t.Fatalf("DeleteInvite last = folder %+v changed %v err %v, want cleared flag", folder, changed, err)
|
||||
}
|
||||
persisted, found, err = dialogs.GetFolder(ctx, ownerID, 2)
|
||||
if err != nil || !found || persisted.HasMyInvites {
|
||||
t.Fatalf("folder after last delete = %+v found %v err %v, want has_my_invites=false", persisted, found, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedFolderSlugValidation(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
svc := NewService(memory.NewChatlistStore(), memory.NewDialogStore())
|
||||
if _, err := svc.CheckInvite(ctx, 2002, "bad!"); !errors.Is(err, domain.ErrChatlistInviteInvalid) {
|
||||
t.Fatalf("CheckInvite bad slug err = %v, want ErrChatlistInviteInvalid", err)
|
||||
}
|
||||
longSlug := strings.Repeat("a", links.MaxChatlistSlugBytes+1)
|
||||
if _, err := svc.JoinInvite(ctx, 2002, longSlug, nil, 100); !errors.Is(err, domain.ErrChatlistInviteInvalid) {
|
||||
t.Fatalf("JoinInvite long slug err = %v, want ErrChatlistInviteInvalid", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRevokedSharedFolderInviteRemainsListedButCannotBeImported(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
chatlists := memory.NewChatlistStore()
|
||||
svc := NewService(chatlists, dialogs, WithSlugGenerator(func() (string, error) { return "slug-revoke", nil }))
|
||||
|
||||
ownerID := int64(1001)
|
||||
viewerID := int64(2002)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed owner folder: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "Main link", []domain.DialogFolderPeer{peer}, 100); err != nil {
|
||||
t.Fatalf("ExportInvite: %v", err)
|
||||
}
|
||||
revoked, err := svc.EditInvite(ctx, ownerID, 2, "slug-revoke", nil, nil, true)
|
||||
if err != nil {
|
||||
t.Fatalf("EditInvite revoke: %v", err)
|
||||
}
|
||||
if !revoked.Revoked {
|
||||
t.Fatalf("revoked invite = %+v, want Revoked=true", revoked)
|
||||
}
|
||||
invites, err := svc.ListInvites(ctx, ownerID, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("ListInvites: %v", err)
|
||||
}
|
||||
if len(invites) != 1 || !invites[0].Revoked {
|
||||
t.Fatalf("listed invites = %+v, want revoked invite visible to owner", invites)
|
||||
}
|
||||
if _, err := svc.CheckInvite(ctx, viewerID, "slug-revoke"); !errors.Is(err, domain.ErrChatlistInviteExpired) {
|
||||
t.Fatalf("CheckInvite revoked err = %v, want ErrChatlistInviteExpired", err)
|
||||
}
|
||||
if _, err := svc.JoinInvite(ctx, viewerID, "slug-revoke", nil, 101); !errors.Is(err, domain.ErrChatlistInviteExpired) {
|
||||
t.Fatalf("JoinInvite revoked err = %v, want ErrChatlistInviteExpired", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedFolderJoinUpdatesAndLeaveUseChannelMemberships(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
channels := &fakeChatlistChannels{}
|
||||
svc := NewService(
|
||||
memory.NewChatlistStore(),
|
||||
dialogs,
|
||||
WithChannels(channels),
|
||||
WithSlugGenerator(func() (string, error) { return "slug-channel", nil }),
|
||||
)
|
||||
|
||||
ownerID := int64(1001)
|
||||
viewerID := int64(2002)
|
||||
peerA := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
peerB := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3002}, AccessHash: 32}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IncludePeers: []domain.DialogFolderPeer{peerA, peerB},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed owner folder: %v", err)
|
||||
}
|
||||
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "Main link", []domain.DialogFolderPeer{peerA, peerB}, 100); err != nil {
|
||||
t.Fatalf("ExportInvite: %v", err)
|
||||
}
|
||||
if got := channels.getIDs; len(got) != 2 || got[0] != peerA.Peer.ID || got[1] != peerB.Peer.ID {
|
||||
t.Fatalf("shareability channel checks = %v, want peerA/peerB", got)
|
||||
}
|
||||
|
||||
joined, err := svc.JoinInvite(ctx, viewerID, "slug-channel", []domain.DialogFolderPeer{peerA}, 101)
|
||||
if err != nil {
|
||||
t.Fatalf("JoinInvite peerA: %v", err)
|
||||
}
|
||||
if got := channels.inviteIDs; len(got) != 1 || got[0] != peerA.Peer.ID {
|
||||
t.Fatalf("invite channel calls = %v, want peerA", got)
|
||||
}
|
||||
if len(joined.ChannelResults) != 1 || joined.ChannelResults[0].Channel.ID != peerA.Peer.ID {
|
||||
t.Fatalf("join channel results = %+v, want peerA result", joined.ChannelResults)
|
||||
}
|
||||
|
||||
updated, err := svc.JoinUpdates(ctx, viewerID, joined.Folder.ID, []domain.DialogFolderPeer{peerB}, 102)
|
||||
if err != nil {
|
||||
t.Fatalf("JoinUpdates peerB: %v", err)
|
||||
}
|
||||
if got := channels.inviteIDs; len(got) != 2 || got[1] != peerB.Peer.ID {
|
||||
t.Fatalf("invite channel calls after updates = %v, want peerB appended", got)
|
||||
}
|
||||
if len(updated.ChannelResults) != 1 || updated.ChannelResults[0].Channel.ID != peerB.Peer.ID {
|
||||
t.Fatalf("update channel results = %+v, want peerB result", updated.ChannelResults)
|
||||
}
|
||||
|
||||
leave, err := svc.Leave(ctx, viewerID, joined.Folder.ID, []domain.DialogFolderPeer{peerA}, 103)
|
||||
if err != nil {
|
||||
t.Fatalf("Leave peerA: %v", err)
|
||||
}
|
||||
if got := channels.leaveIDs; len(got) != 1 || got[0] != peerA.Peer.ID {
|
||||
t.Fatalf("leave channel calls = %v, want peerA", got)
|
||||
}
|
||||
if len(leave.ChannelResults) != 1 || leave.ChannelResults[0].Channel.ID != peerA.Peer.ID {
|
||||
t.Fatalf("leave channel results = %+v, want peerA result", leave.ChannelResults)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSharedFolderPublicPeerFallsBackToSelfJoin(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
channels := &fakeChatlistChannels{publicOnly: true}
|
||||
svc := NewService(
|
||||
memory.NewChatlistStore(),
|
||||
dialogs,
|
||||
WithChannels(channels),
|
||||
WithSlugGenerator(func() (string, error) { return "slug-public", nil }),
|
||||
)
|
||||
|
||||
ownerID := int64(1001)
|
||||
viewerID := int64(2002)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Public",
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed owner folder: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 100); err != nil {
|
||||
t.Fatalf("ExportInvite: %v", err)
|
||||
}
|
||||
if _, err := svc.JoinInvite(ctx, viewerID, "slug-public", []domain.DialogFolderPeer{peer}, 101); err != nil {
|
||||
t.Fatalf("JoinInvite public peer: %v", err)
|
||||
}
|
||||
if len(channels.inviteIDs) != 0 {
|
||||
t.Fatalf("invite channel calls = %v, want none for public-only owner", channels.inviteIDs)
|
||||
}
|
||||
if got := channels.joinIDs; len(got) != 1 || got[0] != peer.Peer.ID {
|
||||
t.Fatalf("join channel calls = %v, want self-join peer", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportInviteRejectsRuleBasedFolder(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
svc := NewService(memory.NewChatlistStore(), dialogs, WithSlugGenerator(func() (string, error) { return "slug-two", nil }))
|
||||
ownerID := int64(1001)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Rule based",
|
||||
Groups: true,
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed folder: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 0); !errors.Is(err, domain.ErrChatlistNotShareable) {
|
||||
t.Fatalf("ExportInvite rule folder err = %v, want ErrChatlistNotShareable", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportInviteRejectsUserPeers(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
svc := NewService(memory.NewChatlistStore(), dialogs, WithSlugGenerator(func() (string, error) { return "slug-user-peer", nil }))
|
||||
ownerID := int64(1001)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 2002}, AccessHash: 22}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Users",
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed folder: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 0); !errors.Is(err, domain.ErrChatlistNotShareable) {
|
||||
t.Fatalf("ExportInvite user peer err = %v, want ErrChatlistNotShareable", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatlistInviteLimitUsesPremiumTier(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dialogs := memory.NewDialogStore()
|
||||
chatlists := memory.NewChatlistStore()
|
||||
ownerID := int64(1001)
|
||||
peer := domain.DialogFolderPeer{Peer: domain.Peer{Type: domain.PeerTypeChannel, ID: 3001}, AccessHash: 31}
|
||||
if err := dialogs.UpsertFolder(ctx, ownerID, domain.DialogFolder{
|
||||
ID: 2,
|
||||
Title: "Team",
|
||||
IncludePeers: []domain.DialogFolderPeer{peer},
|
||||
}); err != nil {
|
||||
t.Fatalf("seed folder: %v", err)
|
||||
}
|
||||
|
||||
nextSlug := 0
|
||||
slugger := func() (string, error) {
|
||||
nextSlug++
|
||||
return "slug-limit-" + string(rune('a'+nextSlug)), nil
|
||||
}
|
||||
svc := NewService(chatlists, dialogs, WithSlugGenerator(slugger))
|
||||
for i := 0; i < domain.MaxChatlistInvitesDefault; i++ {
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, i+1); err != nil {
|
||||
t.Fatalf("ExportInvite default #%d: %v", i+1, err)
|
||||
}
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 10); !errors.Is(err, domain.ErrChatlistInvitesTooMuch) {
|
||||
t.Fatalf("ExportInvite over default limit err = %v, want ErrChatlistInvitesTooMuch", err)
|
||||
}
|
||||
if _, err := svc.EditInvite(ctx, ownerID, 2, "slug-limit-b", nil, nil, true); err != nil {
|
||||
t.Fatalf("EditInvite revoke first default link: %v", err)
|
||||
}
|
||||
if _, _, err := svc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 11); err != nil {
|
||||
t.Fatalf("ExportInvite after revoked link freed active limit: %v", err)
|
||||
}
|
||||
|
||||
premiumSvc := NewService(
|
||||
chatlists,
|
||||
dialogs,
|
||||
WithSlugGenerator(slugger),
|
||||
WithPremiumChecker(func(context.Context, int64) bool { return true }),
|
||||
)
|
||||
if _, _, err := premiumSvc.ExportInvite(ctx, ownerID, 2, "", []domain.DialogFolderPeer{peer}, 11); err != nil {
|
||||
t.Fatalf("ExportInvite premium extra link: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeChatlistChannels struct {
|
||||
getIDs []int64
|
||||
inviteIDs []int64
|
||||
joinIDs []int64
|
||||
leaveIDs []int64
|
||||
publicOnly bool
|
||||
}
|
||||
|
||||
func (f *fakeChatlistChannels) GetChannel(_ context.Context, userID, channelID int64) (domain.ChannelView, error) {
|
||||
f.getIDs = append(f.getIDs, channelID)
|
||||
role := domain.ChannelRoleCreator
|
||||
if f.publicOnly {
|
||||
role = domain.ChannelRoleMember
|
||||
}
|
||||
return domain.ChannelView{
|
||||
Channel: domain.Channel{
|
||||
ID: channelID,
|
||||
AccessHash: channelID * 10,
|
||||
Title: "Team",
|
||||
Username: "team",
|
||||
Megagroup: true,
|
||||
},
|
||||
Self: domain.ChannelMember{
|
||||
ChannelID: channelID,
|
||||
UserID: userID,
|
||||
Role: role,
|
||||
Status: domain.ChannelMemberActive,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakeChatlistChannels) InviteToChannel(_ context.Context, userID, channelID int64, userIDs []int64, date int) (domain.CreateChannelResult, error) {
|
||||
f.inviteIDs = append(f.inviteIDs, channelID)
|
||||
if len(userIDs) == 0 {
|
||||
return domain.CreateChannelResult{}, domain.ErrUsersTooMuch
|
||||
}
|
||||
return fakeChatlistChannelResult(userIDs[0], channelID, domain.ChannelMemberActive, date), nil
|
||||
}
|
||||
|
||||
func (f *fakeChatlistChannels) JoinChannel(_ context.Context, userID, channelID int64, date int) (domain.CreateChannelResult, error) {
|
||||
f.joinIDs = append(f.joinIDs, channelID)
|
||||
return fakeChatlistChannelResult(userID, channelID, domain.ChannelMemberActive, date), nil
|
||||
}
|
||||
|
||||
func (f *fakeChatlistChannels) LeaveChannel(_ context.Context, userID, channelID int64, date int) (domain.CreateChannelResult, error) {
|
||||
f.leaveIDs = append(f.leaveIDs, channelID)
|
||||
return fakeChatlistChannelResult(userID, channelID, domain.ChannelMemberLeft, date), nil
|
||||
}
|
||||
|
||||
func fakeChatlistChannelResult(userID, channelID int64, status domain.ChannelMemberStatus, date int) domain.CreateChannelResult {
|
||||
return domain.CreateChannelResult{
|
||||
Channel: domain.Channel{
|
||||
ID: channelID,
|
||||
AccessHash: channelID * 10,
|
||||
Title: "Team",
|
||||
Username: "team",
|
||||
Megagroup: true,
|
||||
Date: date,
|
||||
},
|
||||
Members: []domain.ChannelMember{{
|
||||
ChannelID: channelID,
|
||||
UserID: userID,
|
||||
Role: domain.ChannelRoleMember,
|
||||
Status: status,
|
||||
JoinedAt: date,
|
||||
}},
|
||||
Recipients: []int64{userID},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue