package system import ( "context" "errors" "testing" ) type defaultRouterUserRepo struct { UserRepo hasMenu bool err error } type loginUserRepo struct { UserRepo user *User err error } func (r *loginUserRepo) FindUserByUsername(context.Context, string) (*User, error) { return r.user, r.err } func (r *defaultRouterUserRepo) HasAuthorityMenu(context.Context, uint, string) (bool, error) { return r.hasMenu, r.err } func TestFallbackDefaultRouterMatchesMenuLookupOutcome(t *testing.T) { user := &User{AuthorityID: 888, Authority: Authority{DefaultRouter: "dashboard"}} NewUserUsecase(&defaultRouterUserRepo{hasMenu: false}).fallbackDefaultRouter(context.Background(), user) if user.Authority.DefaultRouter != "404" { t.Fatalf("missing default route = %q", user.Authority.DefaultRouter) } user.Authority.DefaultRouter = "dashboard" NewUserUsecase(&defaultRouterUserRepo{err: errors.New("database unavailable")}).fallbackDefaultRouter(context.Background(), user) if user.Authority.DefaultRouter != "dashboard" { t.Fatalf("lookup error changed default route to %q", user.Authority.DefaultRouter) } user.Authority.DefaultRouter = "" NewUserUsecase(&defaultRouterUserRepo{}).fallbackDefaultRouter(context.Background(), user) if user.Authority.DefaultRouter != "404" { t.Fatalf("empty missing default route = %q", user.Authority.DefaultRouter) } } func TestLoginRejectsMissingUserWithoutPanic(t *testing.T) { user, err := NewUserUsecase(&loginUserRepo{}).Login(context.Background(), "missing", "password") if !errors.Is(err, ErrInvalidCredentials) || user != nil { t.Fatalf("Login() = %#v, %v; want nil, ErrInvalidCredentials", user, err) } } func TestSetUserAuthoritiesRejectsEmptyListAsBadRequest(t *testing.T) { err := NewUserUsecase(&loginUserRepo{}).SetUserAuthorities(context.Background(), 1, nil) if !errors.Is(err, ErrAuthoritiesRequired) || errors.Is(err, ErrInvalidCredentials) { t.Fatalf("SetUserAuthorities() error = %v, want ErrAuthoritiesRequired", err) } } type statusUpdateRepo struct { UserRepo current, next int created *User } func (r *statusUpdateRepo) UpdateUserWithStatus(_ context.Context, _ *User, policy func(int) (int, error)) error { var err error r.next, err = policy(r.current) return err } func (r *statusUpdateRepo) UpdateSelfUserWithStatus(ctx context.Context, user *User, policy func(int) (int, error)) error { return r.UpdateUserWithStatus(ctx, user, policy) } func (r *statusUpdateRepo) CreateUserWithAuthorities(_ context.Context, user *User, _ []uint) (*User, error) { r.created = user return user, nil } func TestNativeEnablePreservesBusinessReviewStates(t *testing.T) { for _, tc := range []struct{ current, enable, want int }{{0, 2, 1}, {1, 1, 0}, {2, 0, 2}, {3, 2, 3}, {2, 1, 0}, {3, 1, 0}} { repo := &statusUpdateRepo{current: tc.current} if err := NewUserUsecase(repo).UpdateUser(context.Background(), &User{ID: 1, Enable: tc.enable}, nil); err != nil || repo.next != tc.want { t.Fatalf("商务状态与启停不同步: %+v next=%d err=%v", tc, repo.next, err) } } } func TestNativeCreationStatusMatchesDefaultAndDisabledEnable(t *testing.T) { for _, tc := range []struct{ enable, wantEnable, wantStatus int }{{0, 1, 0}, {1, 1, 0}, {2, 2, 1}} { repo := &statusUpdateRepo{} _, err := NewUserUsecase(repo).CreateUser(context.Background(), &User{Username: "test", Password: "test-password", Enable: tc.enable}, nil) if err != nil || repo.created.Enable != tc.wantEnable || repo.created.BusinessStatus != tc.wantStatus { t.Fatalf("新账号状态错误: %+v %v", repo.created, err) } } } func TestSelfProfileCannotApproveOrReenableBusinessState(t *testing.T) { for _, status := range []int{1, 2, 3} { repo := &statusUpdateRepo{current: status} if err := NewUserUsecase(repo).UpdateSelfUser(context.Background(), &User{ID: 1, Enable: 1}); err != ErrUserDisabled || repo.next != status { t.Fatal("个人资料修改越过了停用/审核状态") } } repo := &statusUpdateRepo{current: 0} if err := NewUserUsecase(repo).UpdateSelfUser(context.Background(), &User{ID: 1, Enable: 2}); err != nil || repo.next != 1 { t.Fatal("个人资料停用字段没有同步商务状态") } }