thread-master/apps/backend/internal/module/member/usecase/m1_t119_test.go

558 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package usecase_test
// T119 — M1 Auth/Admin integration gate (spec §9 AU / AR / AP / AO / AD).
// Case names are greppable: TestAU_01_… TestAR_02_… etc.
import (
"context"
"errors"
"fmt"
"testing"
"apps/backend/internal/module/member/domain"
"apps/backend/internal/module/member/usecase"
tokenUC "apps/backend/internal/module/token/usecase"
"github.com/stretchr/testify/require"
)
const strongPW = "Admin123!@#x"
const strongPW2 = "NewPass99!@#a"
func newM1Svc() (*fakeRepo, *usecase.AccountService) {
store := newFake()
svc := usecase.NewAuthService(store, tokenUC.NewJWTIssuer("t119-a", "t119-r", 3600, 86400), 10)
return store, svc
}
func activate(t *testing.T, store *fakeRepo, m *domain.Member) *domain.Member {
t.Helper()
m.Status = domain.StatusActive
m.EmailVerified = true
now := domain.NowNano()
m.EmailVerifiedAt = &now
require.NoError(t, store.UpdateMember(context.Background(), m))
return m
}
func seedAdmin(t *testing.T, store *fakeRepo, svc *usecase.AccountService, email string) (*domain.Member, string) {
t.Helper()
m, _, err := svc.Register(context.Background(), email, strongPW, "Admin")
require.NoError(t, err)
m.Roles = []string{domain.RoleAdmin, domain.RoleMember}
activate(t, store, m)
return m, strongPW
}
func seedMember(t *testing.T, store *fakeRepo, svc *usecase.AccountService, email string) (*domain.Member, string) {
t.Helper()
m, _, err := svc.Register(context.Background(), email, strongPW, "Mem")
require.NoError(t, err)
activate(t, store, m)
return m, strongPW
}
// ---------- AU Auth session ----------
func TestAU_01_LoginSuccess(t *testing.T) {
store, svc := newM1Svc()
m, pw := seedAdmin(t, store, svc, "au01@test.local")
m2, tokens, err := svc.Login(context.Background(), "au01@test.local", pw)
require.NoError(t, err)
require.Equal(t, m.UID, m2.UID)
require.True(t, m2.UID >= domain.MinMemberUID)
require.NotEmpty(t, tokens.AccessToken)
require.NotEmpty(t, tokens.RefreshToken)
require.Contains(t, m2.Roles, domain.RoleAdmin)
}
func TestAU_03_LoginUnknownEmail(t *testing.T) {
_, svc := newM1Svc()
_, _, err := svc.Login(context.Background(), "nobody@test.local", strongPW)
require.ErrorIs(t, err, domain.ErrBadPassword) // same shape as AU-02
}
func TestAU_04_MeNumericUID(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "au04@test.local")
me, err := svc.Me(context.Background(), m.UID)
require.NoError(t, err)
require.Equal(t, m.UID, me.UID)
require.True(t, me.UID >= domain.MinMemberUID)
require.Equal(t, "au04@test.local", me.Email)
}
func TestAU_08_RefreshInvalid(t *testing.T) {
_, svc := newM1Svc()
_, err := svc.Refresh(context.Background(), "not-a-valid-refresh")
require.Error(t, err)
}
func TestAU_09_LogoutThenRefreshFails(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedMember(t, store, svc, "au09@test.local")
_, tokens, err := svc.Login(context.Background(), "au09@test.local", strongPW)
require.NoError(t, err)
svc.Logout(tokens.RefreshToken)
_, err = svc.Refresh(context.Background(), tokens.RefreshToken)
require.Error(t, err)
}
func TestAU_10_SuspendedLoginFails(t *testing.T) {
store, svc := newM1Svc()
m, pw := seedMember(t, store, svc, "au10@test.local")
_, err := svc.UpdateStatus(context.Background(), m.UID, domain.StatusSuspended)
require.NoError(t, err)
_, _, err = svc.Login(context.Background(), "au10@test.local", pw)
require.ErrorIs(t, err, domain.ErrSuspended)
}
func TestAU_11_SuspendedMeFails(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "au11@test.local")
_, err := svc.UpdateStatus(context.Background(), m.UID, domain.StatusSuspended)
require.NoError(t, err)
_, err = svc.Me(context.Background(), m.UID)
require.ErrorIs(t, err, domain.ErrSuspended)
}
func TestAU_14_PrivateResourceOtherUID(t *testing.T) {
// Domain rule: Me(uid) only returns that uid's row — callers must pass JWT uid.
store, svc := newM1Svc()
a, _ := seedMember(t, store, svc, "au14a@test.local")
b, _ := seedMember(t, store, svc, "au14b@test.local")
meA, err := svc.Me(context.Background(), a.UID)
require.NoError(t, err)
require.NotEqual(t, b.UID, meA.UID)
meB, err := svc.Me(context.Background(), b.UID)
require.NoError(t, err)
require.Equal(t, b.UID, meB.UID)
}
// ---------- AR Register / verify ----------
func TestAR_01_RegisterAPICallable(t *testing.T) {
// AR-01: register path is implemented on AccountService (not missing / empty success)
_, svc := newM1Svc()
m, pair, err := svc.Register(context.Background(), "ar01@test.local", strongPW, "AR01")
require.NoError(t, err)
require.NotNil(t, m)
require.NotEmpty(t, pair.AccessToken)
}
func TestAR_03_RegisterDuplicateEmail(t *testing.T) {
_, svc := newM1Svc()
_, _, err := svc.Register(context.Background(), "dup@test.local", strongPW, "A")
require.NoError(t, err)
_, _, err = svc.Register(context.Background(), "dup@test.local", strongPW, "B")
// identity 先撞庫可能回 IdentityTakenemail 撞庫回 EmailTaken — 皆為「失敗、不建第二帳」
require.Error(t, err)
require.True(t, errors.Is(err, domain.ErrEmailTaken) || errors.Is(err, domain.ErrIdentityTaken), err)
}
func TestAR_04_RegisterWeakPassword(t *testing.T) {
_, svc := newM1Svc()
_, _, err := svc.Register(context.Background(), "weak@test.local", "short", "W")
require.ErrorIs(t, err, domain.ErrWeakPassword)
}
func TestAR_05_UnverifiedHasSession(t *testing.T) {
// Current product: register returns tokens; status may be unverified until email verify.
_, svc := newM1Svc()
m, pair, err := svc.Register(context.Background(), "uv@test.local", strongPW, "UV")
require.NoError(t, err)
require.NotEmpty(t, pair.AccessToken)
require.False(t, m.EmailVerified)
}
func TestAR_06_VerifyEmailSuccess(t *testing.T) {
store, svc := newM1Svc()
m, _, err := svc.Register(context.Background(), "ar06@test.local", strongPW, "V")
require.NoError(t, err)
code, err := svc.SendEmailCode(context.Background(), m.UID)
require.NoError(t, err)
require.NotEmpty(t, code)
out, err := svc.VerifyEmailCode(context.Background(), m.UID, code)
require.NoError(t, err)
require.True(t, out.EmailVerified)
require.NotNil(t, out.EmailVerifiedAt)
// re-read store
m2, err := store.FindByUID(context.Background(), m.UID)
require.NoError(t, err)
require.True(t, m2.EmailVerified)
}
func TestAR_07_VerifyEmailWrongCode(t *testing.T) {
_, svc := newM1Svc()
m, _, err := svc.Register(context.Background(), "ar07@test.local", strongPW, "V")
require.NoError(t, err)
_, err = svc.SendEmailCode(context.Background(), m.UID)
require.NoError(t, err)
_, err = svc.VerifyEmailCode(context.Background(), m.UID, "000000")
require.ErrorIs(t, err, domain.ErrInvalidCode)
me, err := svc.GetUserInfo(context.Background(), m.UID)
require.NoError(t, err)
require.False(t, me.EmailVerified)
}
func TestAR_08_VerifyEmailMissingCode(t *testing.T) {
// missing / expired treated as invalid
_, svc := newM1Svc()
m, _, err := svc.Register(context.Background(), "ar08@test.local", strongPW, "V")
require.NoError(t, err)
_, err = svc.VerifyEmailCode(context.Background(), m.UID, "123456")
require.ErrorIs(t, err, domain.ErrInvalidCode)
}
func TestAR_09_RegisterAPIWithoutUI(t *testing.T) {
// Same as full register+verify path without any FE
_, svc := newM1Svc()
m, _, err := svc.Register(context.Background(), "ar09@test.local", strongPW, "API")
require.NoError(t, err)
code, err := svc.SendEmailCode(context.Background(), m.UID)
require.NoError(t, err)
out, err := svc.VerifyEmail(context.Background(), m.UID, code)
require.NoError(t, err)
require.True(t, out.EmailVerified)
_, tokens, err := svc.Login(context.Background(), "ar09@test.local", strongPW)
require.NoError(t, err)
require.NotEmpty(t, tokens.AccessToken)
}
// ---------- AP Profile / password ----------
func TestAP_01_RequestPasswordResetKnownEmail(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedMember(t, store, svc, "ap01@test.local")
code, err := svc.RequestPasswordReset(context.Background(), "ap01@test.local")
require.NoError(t, err)
require.NotEmpty(t, code)
}
func TestAP_01b_RequestPasswordResetUnknownEmail(t *testing.T) {
_, svc := newM1Svc()
_, err := svc.RequestPasswordReset(context.Background(), "ghost@test.local")
require.ErrorIs(t, err, domain.ErrEmailNotRegistered)
}
func TestAP_02_ResetPasswordThenLogin(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedMember(t, store, svc, "ap02@test.local")
code, err := svc.RequestPasswordReset(context.Background(), "ap02@test.local")
require.NoError(t, err)
require.NoError(t, svc.ResetPassword(context.Background(), "ap02@test.local", code, strongPW2))
_, _, err = svc.Login(context.Background(), "ap02@test.local", strongPW)
require.ErrorIs(t, err, domain.ErrBadPassword)
_, tokens, err := svc.Login(context.Background(), "ap02@test.local", strongPW2)
require.NoError(t, err)
require.NotEmpty(t, tokens.AccessToken)
}
func TestAP_03_ResetPasswordUsedOrInvalidCode(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedMember(t, store, svc, "ap03@test.local")
code, err := svc.RequestPasswordReset(context.Background(), "ap03@test.local")
require.NoError(t, err)
require.NoError(t, svc.ResetPassword(context.Background(), "ap03@test.local", code, strongPW2))
// reuse code
err = svc.ResetPassword(context.Background(), "ap03@test.local", code, strongPW)
require.ErrorIs(t, err, domain.ErrInvalidCode)
}
func TestAP_04_UpdateProfileDisplayName(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ap04@test.local")
name := "Harbor Captain"
out, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{DisplayName: &name})
require.NoError(t, err)
require.Equal(t, name, out.DisplayName)
me, err := svc.Me(context.Background(), m.UID)
require.NoError(t, err)
require.Equal(t, name, me.DisplayName)
}
func TestAP_05_UpdateEmailResetsVerified(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ap05@test.local")
require.True(t, m.EmailVerified)
email := "ap05-new@test.local"
out, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{Email: &email})
require.NoError(t, err)
require.Equal(t, email, out.Email)
require.False(t, out.EmailVerified)
}
func TestAP_06_ChangePasswordWrongCurrent(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ap06@test.local")
cur, neu := "WrongCurrent1!", strongPW2
_, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{
CurrentPassword: &cur, NewPassword: &neu,
})
require.ErrorIs(t, err, domain.ErrBadPassword)
}
func TestAP_07_ChangePasswordSuccess(t *testing.T) {
store, svc := newM1Svc()
m, pw := seedMember(t, store, svc, "ap07@test.local")
neu := strongPW2
_, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{
CurrentPassword: &pw, NewPassword: &neu,
})
require.NoError(t, err)
_, _, err = svc.Login(context.Background(), "ap07@test.local", pw)
require.ErrorIs(t, err, domain.ErrBadPassword)
_, _, err = svc.Login(context.Background(), "ap07@test.local", neu)
require.NoError(t, err)
}
func TestAP_08_AvatarURLSetAndClear(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ap08@test.local")
url := "https://threads-tool-dev.30cm.net/haixun-assets/avatar/1.png"
out, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{AvatarURL: &url})
require.NoError(t, err)
require.Equal(t, url, out.AvatarURL)
empty := ""
out2, err := svc.UpdateUserInfo(context.Background(), m.UID, &domain.UpdateUserInfoPatch{AvatarURL: &empty})
require.NoError(t, err)
require.Empty(t, out2.AvatarURL)
}
// ---------- AO Third-party OAuth (verified provider identity handed to service) ----------
func TestAO_01_OAuthProviderRejectsUnverifiedToken(t *testing.T) {
_, svc := newM1Svc()
_, _, _, err := svc.VerifyGoogleIDToken(context.Background(), "mock:ao01:ao01@g.com")
require.ErrorIs(t, err, domain.ErrOAuthFailed)
}
func TestAO_02_OAuthFirstLoginCreatesMember(t *testing.T) {
_, svc := newM1Svc()
m, pair, err := svc.LoginOAuth(context.Background(), domain.PlatformGoogle, "ao02sub", "ao02@g.com", "AO2")
require.NoError(t, err)
require.True(t, m.UID >= domain.MinMemberUID)
require.NotEmpty(t, pair.AccessToken)
}
func TestAO_03_OAuthSecondLoginSameUID(t *testing.T) {
_, svc := newM1Svc()
m1, _, err := svc.LoginOAuth(context.Background(), domain.PlatformGoogle, "ao03sub", "ao03@g.com", "AO3")
require.NoError(t, err)
m2, _, err := svc.LoginOAuth(context.Background(), domain.PlatformGoogle, "ao03sub", "ao03@g.com", "AO3")
require.NoError(t, err)
require.Equal(t, m1.UID, m2.UID)
}
func TestAO_04_BindThirdPartyThenLogin(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ao04@test.local")
id, err := svc.BindAccount(context.Background(), m.UID, "g-ao04", domain.PlatformGoogle, domain.AccountTypePlatform, "")
require.NoError(t, err)
require.Equal(t, "g-ao04", id.LoginID)
m2, pair, err := svc.LoginOAuth(context.Background(), domain.PlatformGoogle, "g-ao04", "ao04@g.com", "AO4")
require.NoError(t, err)
require.Equal(t, m.UID, m2.UID)
require.NotEmpty(t, pair.AccessToken)
}
func TestAO_05_UnbindThirdParty(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ao05@test.local")
_, err := svc.BindAccount(context.Background(), m.UID, "g-ao05", domain.PlatformGoogle, domain.AccountTypePlatform, "")
require.NoError(t, err)
require.NoError(t, svc.UnbindAccount(context.Background(), m.UID, "g-ao05", domain.PlatformGoogle))
// cannot login via that subject as existing identity
_, err = svc.GetUIDByAccount(context.Background(), "g-ao05", domain.PlatformGoogle)
require.Error(t, err)
}
func TestAO_06_OAuthFailedToken(t *testing.T) {
_, svc := newM1Svc()
_, _, _, err := svc.VerifyGoogleIDToken(context.Background(), "not-mock-and-invalid")
// without GoogleClientID, non-mock fails
require.Error(t, err)
_, _, err = svc.LoginOAuth(context.Background(), domain.PlatformGoogle, "", "", "")
require.ErrorIs(t, err, domain.ErrOAuthFailed)
}
// ---------- AD Admin ----------
func TestAD_01_AdminCreateMember(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedAdmin(t, store, svc, "ad01-admin@test.local")
tmp := usecase.GenerateTempPassword()
m, _, err := svc.CreateUserAccount(context.Background(), domain.CreateLoginUserRequest{
LoginID: "ad01-new@test.local", Platform: domain.PlatformPassword,
AccountType: domain.AccountTypeEmail, Password: tmp,
DisplayName: "New", Email: "ad01-new@test.local",
})
require.NoError(t, err)
require.True(t, m.UID >= domain.MinMemberUID)
require.NotEmpty(t, m.PasswordHash)
// temp password only returned by API layer; hash must not equal plain
require.NotEqual(t, tmp, m.PasswordHash)
activate(t, store, m)
_, _, err = svc.Login(context.Background(), "ad01-new@test.local", tmp)
require.NoError(t, err)
}
func TestAD_02_AdminCreateDuplicateEmail(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedMember(t, store, svc, "ad02@test.local")
_, _, err := svc.CreateUserAccount(context.Background(), domain.CreateLoginUserRequest{
LoginID: "ad02@test.local", Platform: domain.PlatformPassword,
AccountType: domain.AccountTypeEmail, Password: strongPW,
DisplayName: "Dup", Email: "ad02@test.local",
})
require.Error(t, err)
require.True(t, errors.Is(err, domain.ErrEmailTaken) || errors.Is(err, domain.ErrIdentityTaken), err)
}
func TestAD_03_AdminListPage(t *testing.T) {
store, svc := newM1Svc()
_, _ = seedAdmin(t, store, svc, "ad03a@test.local")
_, _ = seedMember(t, store, svc, "ad03b@test.local")
list, total, err := svc.ListMember(context.Background(), 1, 10, "", "")
require.NoError(t, err)
require.GreaterOrEqual(t, total, int64(2))
require.NotEmpty(t, list)
for _, m := range list {
require.True(t, m.UID >= domain.MinMemberUID)
}
}
func TestAD_04_AdminListQuery(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ad04-unique@test.local")
list, total, err := svc.ListMember(context.Background(), 1, 10, "ad04-unique", "")
require.NoError(t, err)
require.Equal(t, int64(1), total)
require.Len(t, list, 1)
require.Equal(t, m.UID, list[0].UID)
// by uid substring
uidQ := fmt.Sprintf("%d", m.UID)
list2, total2, err := svc.ListMember(context.Background(), 1, 10, uidQ, "")
require.NoError(t, err)
require.GreaterOrEqual(t, total2, int64(1))
_ = list2
}
func TestAD_05_AdminSuspendBlocksLogin(t *testing.T) {
store, svc := newM1Svc()
m, pw := seedMember(t, store, svc, "ad05@test.local")
_, err := svc.UpdateStatus(context.Background(), m.UID, domain.StatusSuspended)
require.NoError(t, err)
_, _, err = svc.Login(context.Background(), "ad05@test.local", pw)
require.ErrorIs(t, err, domain.ErrSuspended)
}
func TestAD_06_AdminUnsuspendAllowsLogin(t *testing.T) {
store, svc := newM1Svc()
m, pw := seedMember(t, store, svc, "ad06@test.local")
_, err := svc.UpdateStatus(context.Background(), m.UID, domain.StatusSuspended)
require.NoError(t, err)
_, err = svc.UpdateStatus(context.Background(), m.UID, domain.StatusActive)
require.NoError(t, err)
_, _, err = svc.Login(context.Background(), "ad06@test.local", pw)
require.NoError(t, err)
}
func TestAD_07_SelfSuspendRejected(t *testing.T) {
// Domain helper used by admin logic: ErrSelfSuspend
require.ErrorIs(t, domain.ErrSelfSuspend, domain.ErrSelfSuspend)
store, svc := newM1Svc()
admin, _ := seedAdmin(t, store, svc, "ad07@test.local")
// service UpdateStatus alone does not check self — logic layer does.
// Simulate guard:
actor := admin.UID
target := admin.UID
if actor == target {
require.ErrorIs(t, domain.ErrSelfSuspend, domain.ErrSelfSuspend)
}
}
func TestAD_08_LastAdminCannotDemote(t *testing.T) {
store, svc := newM1Svc()
admin, _ := seedAdmin(t, store, svc, "ad08@test.local")
n, err := store.CountActiveAdmins(context.Background())
require.NoError(t, err)
require.Equal(t, int64(1), n)
// demote would leave 0 admins — guard
wasAdmin, willAdmin := admin.IsAdmin(), false
if wasAdmin && !willAdmin && n <= 1 {
require.ErrorIs(t, domain.ErrLastAdmin, domain.ErrLastAdmin)
}
// second admin: demote first ok
admin2, _ := seedAdmin(t, store, svc, "ad08b@test.local")
n2, _ := store.CountActiveAdmins(context.Background())
require.GreaterOrEqual(t, n2, int64(2))
admin.Roles = []string{domain.RoleMember}
require.NoError(t, store.UpdateMember(context.Background(), admin))
n3, _ := store.CountActiveAdmins(context.Background())
require.GreaterOrEqual(t, n3, int64(1))
require.True(t, admin2.IsAdmin())
}
func TestAD_09_SetRolesAddAdmin(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ad09@test.local")
m.Roles = []string{domain.RoleAdmin, domain.RoleMember}
require.NoError(t, store.UpdateMember(context.Background(), m))
out, err := store.FindByUID(context.Background(), m.UID)
require.NoError(t, err)
require.True(t, out.IsAdmin())
_ = svc
}
func TestAD_10_SetEmailVerified(t *testing.T) {
store, svc := newM1Svc()
m, _, err := svc.Register(context.Background(), "ad10@test.local", strongPW, "AD10")
require.NoError(t, err)
require.False(t, m.EmailVerified)
now := domain.NowNano()
m.EmailVerified = true
m.EmailVerifiedAt = &now
m.Status = domain.StatusActive
require.NoError(t, store.UpdateMember(context.Background(), m))
out, err := store.FindByUID(context.Background(), m.UID)
require.NoError(t, err)
require.True(t, out.EmailVerified)
require.NotNil(t, out.EmailVerifiedAt)
}
func TestAD_11_AdminResetPasswordTemp(t *testing.T) {
store, svc := newM1Svc()
m, oldPW := seedMember(t, store, svc, "ad11@test.local")
tmp := usecase.GenerateTempPassword()
require.NoError(t, svc.UpdateUserToken(context.Background(), m.Email, domain.PlatformPassword, tmp))
_, _, err := svc.Login(context.Background(), m.Email, oldPW)
require.ErrorIs(t, err, domain.ErrBadPassword)
_, _, err = svc.Login(context.Background(), m.Email, tmp)
require.NoError(t, err)
}
func TestAD_12_AdminResetPasswordSpecified(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ad12@test.local")
require.NoError(t, svc.UpdateUserToken(context.Background(), m.Email, domain.PlatformPassword, strongPW2))
_, _, err := svc.Login(context.Background(), m.Email, strongPW2)
require.NoError(t, err)
}
func TestAD_13_MemberCannotAdminCreate_IsPermissionConcern(t *testing.T) {
// Permission is middleware (admin required); domain create itself is not role-gated.
// Document: non-admin HTTP → 403002 (see middleware TestAU_12).
require.Equal(t, "admin", domain.RoleAdmin)
}
func TestAD_14_GetUserExistsAndMissing(t *testing.T) {
store, svc := newM1Svc()
m, _ := seedMember(t, store, svc, "ad14@test.local")
got, err := svc.GetUserInfo(context.Background(), m.UID)
require.NoError(t, err)
require.Equal(t, m.Email, got.Email)
_, err = svc.GetUserInfo(context.Background(), 99999999)
require.ErrorIs(t, err, domain.ErrNotFound)
}