From 305eaabb535379e958e3530080314986dcb959ed Mon Sep 17 00:00:00 2001 From: yangjianbo Date: Mon, 29 Dec 2025 15:22:50 +0800 Subject: [PATCH] =?UTF-8?q?test(=E7=94=A8=E6=88=B7):=20=E8=A1=A5=E9=BD=90?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=94=A8=E6=88=B7=E4=B8=8E=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=8D=95=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖管理员创建用户与注册流程的关键失败分支\n完善创建成功路径的断言 --- .../service/admin_service_create_user_test.go | 69 +++++++ .../service/admin_service_delete_test.go | 19 +- .../service/auth_service_register_test.go | 182 ++++++++++++++++++ 3 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 backend/internal/service/admin_service_create_user_test.go create mode 100644 backend/internal/service/auth_service_register_test.go diff --git a/backend/internal/service/admin_service_create_user_test.go b/backend/internal/service/admin_service_create_user_test.go new file mode 100644 index 00000000..cfa52de8 --- /dev/null +++ b/backend/internal/service/admin_service_create_user_test.go @@ -0,0 +1,69 @@ +//go:build unit + +package service + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAdminService_CreateUser_Success(t *testing.T) { + repo := &userRepoStub{nextID: 10} + svc := &adminServiceImpl{userRepo: repo} + + input := &CreateUserInput{ + Email: "user@test.com", + Password: "strong-pass", + Username: "tester", + Wechat: "wx", + Notes: "note", + Balance: 12.5, + Concurrency: 7, + AllowedGroups: []int64{3, 5}, + } + + user, err := svc.CreateUser(context.Background(), input) + require.NoError(t, err) + require.NotNil(t, user) + require.Equal(t, int64(10), user.ID) + require.Equal(t, input.Email, user.Email) + require.Equal(t, input.Username, user.Username) + require.Equal(t, input.Wechat, user.Wechat) + require.Equal(t, input.Notes, user.Notes) + require.Equal(t, input.Balance, user.Balance) + require.Equal(t, input.Concurrency, user.Concurrency) + require.Equal(t, input.AllowedGroups, user.AllowedGroups) + require.Equal(t, RoleUser, user.Role) + require.Equal(t, StatusActive, user.Status) + require.True(t, user.CheckPassword(input.Password)) + require.Len(t, repo.created, 1) + require.Equal(t, user, repo.created[0]) +} + +func TestAdminService_CreateUser_EmailExists(t *testing.T) { + repo := &userRepoStub{createErr: ErrEmailExists} + svc := &adminServiceImpl{userRepo: repo} + + _, err := svc.CreateUser(context.Background(), &CreateUserInput{ + Email: "dup@test.com", + Password: "password", + }) + require.ErrorIs(t, err, ErrEmailExists) + require.Empty(t, repo.created) +} + +func TestAdminService_CreateUser_CreateError(t *testing.T) { + createErr := errors.New("db down") + repo := &userRepoStub{createErr: createErr} + svc := &adminServiceImpl{userRepo: repo} + + _, err := svc.CreateUser(context.Background(), &CreateUserInput{ + Email: "user@test.com", + Password: "password", + }) + require.ErrorIs(t, err, createErr) + require.Empty(t, repo.created) +} diff --git a/backend/internal/service/admin_service_delete_test.go b/backend/internal/service/admin_service_delete_test.go index 80af809b..d27773fa 100644 --- a/backend/internal/service/admin_service_delete_test.go +++ b/backend/internal/service/admin_service_delete_test.go @@ -15,12 +15,24 @@ import ( type userRepoStub struct { user *User getErr error + createErr error deleteErr error + exists bool + existsErr error + nextID int64 + created []*User deletedIDs []int64 } func (s *userRepoStub) Create(ctx context.Context, user *User) error { - panic("unexpected Create call") + if s.createErr != nil { + return s.createErr + } + if s.nextID != 0 && user.ID == 0 { + user.ID = s.nextID + } + s.created = append(s.created, user) + return nil } func (s *userRepoStub) GetByID(ctx context.Context, id int64) (*User, error) { @@ -71,7 +83,10 @@ func (s *userRepoStub) UpdateConcurrency(ctx context.Context, id int64, amount i } func (s *userRepoStub) ExistsByEmail(ctx context.Context, email string) (bool, error) { - panic("unexpected ExistsByEmail call") + if s.existsErr != nil { + return false, s.existsErr + } + return s.exists, nil } func (s *userRepoStub) RemoveGroupFromAllowedGroups(ctx context.Context, groupID int64) (int64, error) { diff --git a/backend/internal/service/auth_service_register_test.go b/backend/internal/service/auth_service_register_test.go new file mode 100644 index 00000000..cd6e2808 --- /dev/null +++ b/backend/internal/service/auth_service_register_test.go @@ -0,0 +1,182 @@ +//go:build unit + +package service + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/Wei-Shaw/sub2api/internal/config" + "github.com/stretchr/testify/require" +) + +type settingRepoStub struct { + values map[string]string + err error +} + +func (s *settingRepoStub) Get(ctx context.Context, key string) (*Setting, error) { + panic("unexpected Get call") +} + +func (s *settingRepoStub) GetValue(ctx context.Context, key string) (string, error) { + if s.err != nil { + return "", s.err + } + if v, ok := s.values[key]; ok { + return v, nil + } + return "", ErrSettingNotFound +} + +func (s *settingRepoStub) Set(ctx context.Context, key, value string) error { + panic("unexpected Set call") +} + +func (s *settingRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) { + panic("unexpected GetMultiple call") +} + +func (s *settingRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error { + panic("unexpected SetMultiple call") +} + +func (s *settingRepoStub) GetAll(ctx context.Context) (map[string]string, error) { + panic("unexpected GetAll call") +} + +func (s *settingRepoStub) Delete(ctx context.Context, key string) error { + panic("unexpected Delete call") +} + +type emailCacheStub struct { + data *VerificationCodeData + err error +} + +func (s *emailCacheStub) GetVerificationCode(ctx context.Context, email string) (*VerificationCodeData, error) { + if s.err != nil { + return nil, s.err + } + return s.data, nil +} + +func (s *emailCacheStub) SetVerificationCode(ctx context.Context, email string, data *VerificationCodeData, ttl time.Duration) error { + return nil +} + +func (s *emailCacheStub) DeleteVerificationCode(ctx context.Context, email string) error { + return nil +} + +func newAuthService(repo *userRepoStub, settings map[string]string, emailCache EmailCache) *AuthService { + cfg := &config.Config{ + JWT: config.JWTConfig{ + Secret: "test-secret", + ExpireHour: 1, + }, + Default: config.DefaultConfig{ + UserBalance: 3.5, + UserConcurrency: 2, + }, + } + + var settingService *SettingService + if settings != nil { + settingService = NewSettingService(&settingRepoStub{values: settings}, cfg) + } + + var emailService *EmailService + if emailCache != nil { + emailService = NewEmailService(&settingRepoStub{values: settings}, emailCache) + } + + return NewAuthService( + repo, + cfg, + settingService, + emailService, + nil, + nil, + ) +} + +func TestAuthService_Register_Disabled(t *testing.T) { + repo := &userRepoStub{} + service := newAuthService(repo, map[string]string{ + SettingKeyRegistrationEnabled: "false", + }, nil) + + _, _, err := service.Register(context.Background(), "user@test.com", "password") + require.ErrorIs(t, err, ErrRegDisabled) +} + +func TestAuthService_Register_EmailVerifyRequired(t *testing.T) { + repo := &userRepoStub{} + service := newAuthService(repo, map[string]string{ + SettingKeyRegistrationEnabled: "true", + SettingKeyEmailVerifyEnabled: "true", + }, nil) + + _, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "") + require.ErrorIs(t, err, ErrEmailVerifyRequired) +} + +func TestAuthService_Register_EmailVerifyInvalid(t *testing.T) { + repo := &userRepoStub{} + cache := &emailCacheStub{ + data: &VerificationCodeData{Code: "expected", Attempts: 0}, + } + service := newAuthService(repo, map[string]string{ + SettingKeyRegistrationEnabled: "true", + SettingKeyEmailVerifyEnabled: "true", + }, cache) + + _, _, err := service.RegisterWithVerification(context.Background(), "user@test.com", "password", "wrong") + require.ErrorIs(t, err, ErrInvalidVerifyCode) + require.ErrorContains(t, err, "verify code") +} + +func TestAuthService_Register_EmailExists(t *testing.T) { + repo := &userRepoStub{exists: true} + service := newAuthService(repo, nil, nil) + + _, _, err := service.Register(context.Background(), "user@test.com", "password") + require.ErrorIs(t, err, ErrEmailExists) +} + +func TestAuthService_Register_CheckEmailError(t *testing.T) { + repo := &userRepoStub{existsErr: errors.New("db down")} + service := newAuthService(repo, nil, nil) + + _, _, err := service.Register(context.Background(), "user@test.com", "password") + require.ErrorIs(t, err, ErrServiceUnavailable) +} + +func TestAuthService_Register_CreateError(t *testing.T) { + repo := &userRepoStub{createErr: errors.New("create failed")} + service := newAuthService(repo, nil, nil) + + _, _, err := service.Register(context.Background(), "user@test.com", "password") + require.ErrorIs(t, err, ErrServiceUnavailable) +} + +func TestAuthService_Register_Success(t *testing.T) { + repo := &userRepoStub{nextID: 5} + service := newAuthService(repo, nil, nil) + + token, user, err := service.Register(context.Background(), "user@test.com", "password") + require.NoError(t, err) + require.NotEmpty(t, token) + require.NotNil(t, user) + require.Equal(t, int64(5), user.ID) + require.Equal(t, "user@test.com", user.Email) + require.Equal(t, RoleUser, user.Role) + require.Equal(t, StatusActive, user.Status) + require.Equal(t, 3.5, user.Balance) + require.Equal(t, 2, user.Concurrency) + require.Len(t, repo.created, 1) + require.True(t, user.CheckPassword("password")) +}