package bootstrap import ( "context" "testing" app "haixun-backend/internal/library/errors" "haixun-backend/internal/library/errors/code" "haixun-backend/internal/model/member/domain/entity" domrepo "haixun-backend/internal/model/member/domain/repository" "golang.org/x/crypto/bcrypt" ) type memoryMemberRepo struct { byEmail map[string]*entity.Member } func memberKey(tenantID, email string) string { return tenantID + ":" + normalizeEmail(email) } func (m *memoryMemberRepo) EnsureIndexes(context.Context) error { return nil } func (m *memoryMemberRepo) Create(_ context.Context, member *entity.Member) (*entity.Member, error) { if m.byEmail == nil { m.byEmail = map[string]*entity.Member{} } key := memberKey(member.TenantID, member.Email) if _, ok := m.byEmail[key]; ok { return nil, app.For(code.Member).ResConflict("member already exists") } cp := *member m.byEmail[key] = &cp return &cp, nil } func (m *memoryMemberRepo) FindByUID(_ context.Context, tenantID, uid string) (*entity.Member, error) { for _, item := range m.byEmail { if item.TenantID == tenantID && item.UID == uid { cp := *item return &cp, nil } } return nil, app.For(code.Member).ResNotFound("member not found") } func (m *memoryMemberRepo) FindByEmail(_ context.Context, tenantID, email string) (*entity.Member, error) { item, ok := m.byEmail[memberKey(tenantID, email)] if !ok { return nil, app.For(code.Member).ResNotFound("member not found") } cp := *item return &cp, nil } func (m *memoryMemberRepo) UpdateProfile(context.Context, string, string, domrepo.ProfileUpdate) (*entity.Member, error) { return nil, app.For(code.Member).SysNotImplemented("not implemented") } func (m *memoryMemberRepo) SetActiveThreadsAccountID(_ context.Context, tenantID, uid, accountID string) error { for _, item := range m.byEmail { if item.TenantID == tenantID && item.UID == uid { item.ActiveThreadsAccountID = accountID return nil } } return app.For(code.Member).ResNotFound("member not found") } func (m *memoryMemberRepo) SetRoles(_ context.Context, tenantID, uid string, roles []string) error { for _, item := range m.byEmail { if item.TenantID == tenantID && item.UID == uid { item.Roles = append([]string(nil), roles...) return nil } } return app.For(code.Member).ResNotFound("member not found") } func TestEnsureAdminMemberCreatesAdmin(t *testing.T) { repo := &memoryMemberRepo{byEmail: map[string]*entity.Member{}} member, created, err := EnsureAdminMember(context.Background(), repo, AdminOptions{ TenantID: "default", Email: "admin@haixun.local", Password: "Admin-Pass-1!", }) if err != nil { t.Fatalf("EnsureAdminMember: %v", err) } if !created { t.Fatal("expected created=true") } if member.Roles[0] != "admin" { t.Fatalf("roles=%v", member.Roles) } if err := bcrypt.CompareHashAndPassword([]byte(member.PasswordHash), []byte("Admin-Pass-1!")); err != nil { t.Fatalf("password hash mismatch: %v", err) } } func TestEnsureAdminMemberUpgradesExisting(t *testing.T) { repo := &memoryMemberRepo{byEmail: map[string]*entity.Member{ memberKey("default", "admin@haixun.local"): { TenantID: "default", UID: "uid-1", Email: "admin@haixun.local", Roles: []string{"user"}, }, }} _, created, err := EnsureAdminMember(context.Background(), repo, AdminOptions{ TenantID: "default", Email: "admin@haixun.local", Password: "Admin-Pass-1!", }) if err != nil { t.Fatalf("EnsureAdminMember: %v", err) } if created { t.Fatal("expected created=false") } member := repo.byEmail[memberKey("default", "admin@haixun.local")] if len(member.Roles) != 1 || member.Roles[0] != "admin" { t.Fatalf("roles=%v", member.Roles) } }