Browse Source

user

master
dark 1 month ago
parent
commit
40e5ccfb70
  1. 3
      .claude/settings.local.json
  2. 6
      backend/go.mod
  3. 4
      backend/go.sum
  4. 274
      backend/internal/logic/profile/changepasswordlogic_test.go
  5. 243
      backend/internal/logic/profile/getprofilelogic_test.go
  6. 269
      backend/internal/logic/profile/updateprofilelogic_test.go
  7. 238
      backend/internal/logic/user/createuserlogic_test.go
  8. 332
      backend/internal/logic/user/deleteuserlogic_test.go
  9. 256
      backend/internal/logic/user/getuserlogic_test.go
  10. 405
      backend/internal/logic/user/updateuserlogic_test.go
  11. 408
      backend/model/profile_model_test.go
  12. 368
      backend/model/user_model_test.go
  13. 166
      backend/run_logic_tests.sh
  14. 4
      backend/run_tests.bat
  15. 2
      backend/tests/user/test_user.sh

3
.claude/settings.local.json

@ -25,7 +25,8 @@
"Bash(tasklist:*)",
"Bash(dir:*)",
"Bash(findstr:*)",
"Bash(netstat:*)"
"Bash(netstat:*)",
"Bash(set CGO_ENABLED=1)"
]
}
}

6
backend/go.mod

@ -4,8 +4,10 @@ go 1.25.0
require (
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/stretchr/testify v1.11.1
github.com/zeromicro/go-zero v1.9.4
gorm.io/driver/mysql v1.6.0
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.1
)
@ -14,6 +16,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@ -28,9 +31,11 @@ require (
github.com/klauspost/compress v1.17.11 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.21.1 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
@ -56,4 +61,5 @@ require (
google.golang.org/grpc v1.65.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

4
backend/go.sum

@ -51,6 +51,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg=
@ -140,6 +142,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=

274
backend/internal/logic/profile/changepasswordlogic_test.go

@ -0,0 +1,274 @@
package profile
import (
"context"
"crypto/md5"
"fmt"
"testing"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.ChangePasswordRequest{}
// TestChangePassword_Success 测试成功修改密码
func TestChangePassword_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户,密码为 "password"
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 200, resp.Code)
assert.Equal(t, "修改密码成功", resp.Message)
// 验证数据库中的密码已更新
updatedUser, err := model.FindOne(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
newEncrypted := fmt.Sprintf("%x", md5.Sum([]byte(req.NewPassword)))
assert.Equal(t, newEncrypted, updatedUser.Password)
}
// TestChangePassword_WrongOldPassword 测试旧密码错误
func TestChangePassword_WrongOldPassword(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据 - 使用错误的旧密码
req := &types.ChangePasswordRequest{
OldPassword: "wrongpassword",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 400, resp.Code)
assert.Equal(t, "旧密码错误", resp.Message)
// 验证数据库中的密码没有改变
updatedUser, err := model.FindOne(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
assert.Equal(t, user.Password, updatedUser.Password)
}
// TestChangePassword_NoUserIdInContext 测试上下文中没有用户ID
func TestChangePassword_NoUserIdInContext(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
createTestUser(t, svcCtx.DB)
// 设置上下文,不包含用户ID
ctx := context.Background()
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "未获取到用户信息")
}
// TestChangePassword_InvalidUserIdType 测试用户ID类型错误
func TestChangePassword_InvalidUserIdType(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
createTestUser(t, svcCtx.DB)
// 设置上下文,用户ID类型错误
ctx := context.WithValue(context.Background(), "userId", "invalid")
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户ID格式错误")
}
// TestChangePassword_UserNotFound 测试用户不存在
func TestChangePassword_UserNotFound(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 设置上下文,使用不存在的用户ID
ctx := context.WithValue(context.Background(), "userId", int64(99999))
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestChangePassword_SamePassword 测试使用相同的新密码
func TestChangePassword_SamePassword(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据 - 新密码与旧密码相同
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "password",
}
// 执行测试 - 虽然相同,但从逻辑上应该允许
resp, err := logic.ChangePassword(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 200, resp.Code)
// 验证密码加密后的值仍然一致
updatedUser, err := model.FindOne(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
assert.Equal(t, user.Password, updatedUser.Password)
}
// TestChangePassword_MultipleChanges 测试多次修改密码
func TestChangePassword_MultipleChanges(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
passwords := []string{"password", "newpass1", "newpass2", "newpass3"}
for i := 0; i < len(passwords)-1; i++ {
logic := NewChangePasswordLogic(ctx, svcCtx)
req := &types.ChangePasswordRequest{
OldPassword: passwords[i],
NewPassword: passwords[i+1],
}
resp, err := logic.ChangePassword(req)
require.NoError(t, err, "Failed at iteration %d", i)
require.NotNil(t, resp)
assert.Equal(t, 200, resp.Code)
// 验证密码已更新
updatedUser, err := model.FindOne(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
expectedEncrypted := fmt.Sprintf("%x", md5.Sum([]byte(passwords[i+1])))
assert.Equal(t, expectedEncrypted, updatedUser.Password)
}
}
// BenchmarkChangePassword 性能测试
func BenchmarkChangePassword(b *testing.B) {
svcCtx, _ := setupTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
// 创建测试用户
user := createTestUser(&testing.T{}, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
logic := NewChangePasswordLogic(ctx, svcCtx)
_, _ = logic.ChangePassword(req)
}
}

243
backend/internal/logic/profile/getprofilelogic_test.go

@ -0,0 +1,243 @@
package profile
import (
"context"
"testing"
"time"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.GetProfileResponse{}
// setupTestDB 创建测试数据库并返回 ServiceContext(使用MySQL)
func setupTestDB(t *testing.T) (*svc.ServiceContext, func()) {
t.Helper()
// 使用 MySQL 进行测试
dsn := "root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
require.NoError(t, err)
// 自动迁移表
err = db.AutoMigrate(&model.User{}, &model.Profile{})
require.NoError(t, err)
// 清理所有测试数据(使用 TRUNCATE 快速清空表)
db.Exec("SET FOREIGN_KEY_CHECKS = 0")
db.Exec("TRUNCATE TABLE profile")
db.Exec("TRUNCATE TABLE user")
db.Exec("SET FOREIGN_KEY_CHECKS = 1")
// 创建测试 ServiceContext
svcCtx := &svc.ServiceContext{
DB: db,
}
// 清理函数
cleanup := func() {
sqlDB, _ := db.DB()
if sqlDB != nil {
sqlDB.Close()
}
}
return svcCtx, cleanup
}
// createTestUser 创建测试用户
func createTestUser(t *testing.T, db *gorm.DB) *model.User {
t.Helper()
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "5f4dcc3b5aa765d61d8327deb882cf99", // MD5 of "password"
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// createTestProfile 创建测试个人资料
func createTestProfile(t *testing.T, db *gorm.DB, userId int64) *model.Profile {
t.Helper()
now := time.Now()
profile := &model.Profile{
UserId: userId,
Avatar: "http://example.com/avatar.jpg",
Bio: "This is a test bio",
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(profile).Error
require.NoError(t, err)
return profile
}
// TestGetProfile_Success 测试成功获取个人信息
func TestGetProfile_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
createTestProfile(t, svcCtx.DB, user.Id)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
assert.Equal(t, "13800138000", resp.Phone)
assert.Equal(t, "http://example.com/avatar.jpg", resp.Avatar)
assert.Equal(t, "This is a test bio", resp.Bio)
assert.Equal(t, 1, resp.Status)
}
// TestGetProfile_NoProfile 测试用户没有个人资料的情况
func TestGetProfile_NoProfile(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户,不创建个人资料
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果 - 应该返回用户信息,但 profile 字段为空
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "", resp.Avatar)
assert.Equal(t, "", resp.Bio)
}
// TestGetProfile_NoUserIdInContext 测试上下文中没有用户ID
func TestGetProfile_NoUserIdInContext(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
createTestUser(t, svcCtx.DB)
// 设置上下文,不包含用户ID
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "未获取到用户信息")
}
// TestGetProfile_InvalidUserIdType 测试用户ID类型错误
func TestGetProfile_InvalidUserIdType(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
createTestUser(t, svcCtx.DB)
// 设置上下文,用户ID类型错误
ctx := context.WithValue(context.Background(), "userId", "invalid")
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户ID格式错误")
}
// TestGetProfile_UserNotFound 测试用户不存在
func TestGetProfile_UserNotFound(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 设置上下文,使用不存在的用户ID
ctx := context.WithValue(context.Background(), "userId", int64(99999))
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// BenchmarkGetProfile 性能测试
func BenchmarkGetProfile(b *testing.B) {
svcCtx, _ := setupTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
// 创建测试用户
user := createTestUser(&testing.T{}, svcCtx.DB)
createTestProfile(&testing.T{}, svcCtx.DB, user.Id)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
b.ResetTimer()
for i := 0; i < b.N; i++ {
logic := NewGetProfileLogic(ctx, svcCtx)
_, _ = logic.GetProfile()
}
}

269
backend/internal/logic/profile/updateprofilelogic_test.go

@ -0,0 +1,269 @@
package profile
import (
"context"
"testing"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.UpdateProfileRequest{}
// TestUpdateProfile_Success 测试成功更新个人资料
func TestUpdateProfile_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Username: "updateduser",
Phone: "13900139000",
Avatar: "http://example.com/newavatar.jpg",
Bio: "Updated bio text",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "updateduser", resp.Username)
assert.Equal(t, "13900139000", resp.Phone)
assert.Equal(t, "http://example.com/newavatar.jpg", resp.Avatar)
assert.Equal(t, "Updated bio text", resp.Bio)
}
// TestUpdateProfile_CreateNewProfile 测试用户第一次更新时创建个人资料
func TestUpdateProfile_CreateNewProfile(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户,但不创建个人资料
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Avatar: "http://example.com/avatar.jpg",
Bio: "First bio",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "http://example.com/avatar.jpg", resp.Avatar)
assert.Equal(t, "First bio", resp.Bio)
// 验证数据库中确实创建了个人资料
profile, err := model.FindProfileByUserId(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
require.NotNil(t, profile)
assert.Equal(t, "http://example.com/avatar.jpg", profile.Avatar)
}
// TestUpdateProfile_UpdateExistingProfile 测试更新已存在的个人资料
func TestUpdateProfile_UpdateExistingProfile(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户和个人资料
user := createTestUser(t, svcCtx.DB)
createTestProfile(t, svcCtx.DB, user.Id)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据 - 只更新部分字段
req := &types.UpdateProfileRequest{
Bio: "Updated bio only",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "Updated bio only", resp.Bio)
assert.Equal(t, "http://example.com/avatar.jpg", resp.Avatar) // 保持不变
// 验证数据库中确实更新了
profile, err := model.FindProfileByUserId(context.Background(), svcCtx.DB, user.Id)
require.NoError(t, err)
assert.Equal(t, "Updated bio only", profile.Bio)
assert.Equal(t, "http://example.com/avatar.jpg", profile.Avatar)
}
// TestUpdateProfile_UpdateOnlyUsername 测试只更新用户名
func TestUpdateProfile_UpdateOnlyUsername(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据 - 只更新用户名
req := &types.UpdateProfileRequest{
Username: "newusername",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "newusername", resp.Username)
assert.Equal(t, "test@example.com", resp.Email) // 保持不变
}
// TestUpdateProfile_NoUserIdInContext 测试上下文中没有用户ID
func TestUpdateProfile_NoUserIdInContext(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
createTestUser(t, svcCtx.DB)
// 设置上下文,不包含用户ID
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Username: "updateduser",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "未获取到用户信息")
}
// TestUpdateProfile_UserNotFound 测试用户不存在
func TestUpdateProfile_UserNotFound(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 设置上下文,使用不存在的用户ID
ctx := context.WithValue(context.Background(), "userId", int64(99999))
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Username: "updateduser",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestUpdateProfile_EmptyRequest 测试空请求
func TestUpdateProfile_EmptyRequest(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备空请求数据
req := &types.UpdateProfileRequest{}
// 执行测试 - 应该正常工作,只是不做任何更新
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
// 验证数据没有变化
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "13800138000", resp.Phone)
}
// BenchmarkUpdateProfile 性能测试
func BenchmarkUpdateProfile(b *testing.B) {
svcCtx, _ := setupTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
// 创建测试用户
user := createTestUser(&testing.T{}, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Username: "updateduser",
Bio: "Updated bio",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
logic := NewUpdateProfileLogic(ctx, svcCtx)
_, _ = logic.UpdateProfile(req)
}
}

238
backend/internal/logic/user/createuserlogic_test.go

@ -0,0 +1,238 @@
package user
import (
"context"
"testing"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// setupUserTestDB 创建测试数据库并返回 ServiceContext(使用MySQL)
func setupUserTestDB(t *testing.T) (*svc.ServiceContext, func()) {
t.Helper()
// 使用 MySQL 进行测试
dsn := "root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
require.NoError(t, err)
// 自动迁移表
err = db.AutoMigrate(&model.User{}, &model.Profile{})
require.NoError(t, err)
// 清理所有测试数据(使用 TRUNCATE 快速清空表)
db.Exec("SET FOREIGN_KEY_CHECKS = 0")
db.Exec("TRUNCATE TABLE profile")
db.Exec("TRUNCATE TABLE user")
db.Exec("SET FOREIGN_KEY_CHECKS = 1")
// 创建测试 ServiceContext
svcCtx := &svc.ServiceContext{
DB: db,
}
// 清理函数
cleanup := func() {
sqlDB, _ := db.DB()
if sqlDB != nil {
sqlDB.Close()
}
}
return svcCtx, cleanup
}
// TestCreateUser_Success 测试成功创建用户
func TestCreateUser_Success(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewCreateUserLogic(ctx, svcCtx)
// 准备创建用户请求数据
req := &types.CreateUserRequest{
Username: "testuser",
Email: "test@example.com",
Password: "password123",
Phone: "13800138000",
}
// 执行测试
resp, err := logic.CreateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Greater(t, resp.Id, int64(0))
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
assert.Equal(t, "13800138000", resp.Phone)
assert.Equal(t, 1, resp.Status)
assert.NotEmpty(t, resp.CreatedAt)
assert.NotEmpty(t, resp.UpdatedAt)
}
// TestCreateUser_DuplicateEmail 测试邮箱重复
func TestCreateUser_DuplicateEmail(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建第一个用户
ctx := context.Background()
logic := NewCreateUserLogic(ctx, svcCtx)
req1 := &types.CreateUserRequest{
Username: "user1",
Email: "test@example.com",
Password: "password123",
}
_, err := logic.CreateUser(req1)
require.NoError(t, err)
// 尝试创建相同邮箱的第二个用户
logic2 := NewCreateUserLogic(ctx, svcCtx)
req2 := &types.CreateUserRequest{
Username: "user2",
Email: "test@example.com", // 相同邮箱
Password: "password456",
}
resp, err := logic2.CreateUser(req2)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "邮箱已被注册")
}
// TestCreateUser_MinimumFields 测试使用最少字段创建用户
func TestCreateUser_MinimumFields(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewCreateUserLogic(ctx, svcCtx)
// 准备创建用户请求数据 - 只填必填字段
req := &types.CreateUserRequest{
Username: "testuser",
Email: "test@example.com",
Password: "password123",
// Phone 字段可选,不填写
}
// 执行测试
resp, err := logic.CreateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
assert.Equal(t, "", resp.Phone) // Phone 应该为空字符串
}
// TestCreateUser_PasswordEncryption 测试密码是否正确加密
func TestCreateUser_PasswordEncryption(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewCreateUserLogic(ctx, svcCtx)
req := &types.CreateUserRequest{
Username: "testuser",
Email: "test@example.com",
Password: "password123",
}
// 执行测试
resp, err := logic.CreateUser(req)
require.NoError(t, err)
require.NotNil(t, resp)
// 查询数据库验证密码已加密(不应该存储明文)
user, err := model.FindOne(context.Background(), svcCtx.DB, resp.Id)
require.NoError(t, err)
assert.NotEqual(t, "password123", user.Password) // 密码应该已被加密
}
// TestCreateUser_MultipleUsers 测试创建多个用户
func TestCreateUser_MultipleUsers(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
ctx := context.Background()
// 创建多个用户
users := []struct {
username string
email string
}{
{"user1", "user1@example.com"},
{"user2", "user2@example.com"},
{"user3", "user3@example.com"},
}
for _, u := range users {
logic := NewCreateUserLogic(ctx, svcCtx)
req := &types.CreateUserRequest{
Username: u.username,
Email: u.email,
Password: "password123",
}
resp, err := logic.CreateUser(req)
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, u.username, resp.Username)
assert.Equal(t, u.email, resp.Email)
}
}
// BenchmarkCreateUser 性能测试
func BenchmarkCreateUser(b *testing.B) {
svcCtx, _ := setupUserTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
ctx := context.Background()
req := &types.CreateUserRequest{
Username: "testuser",
Email: "test@example.com",
Password: "password123",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
// 使用不同的邮箱避免重复
req.Email = "test@example.com"
logic := NewCreateUserLogic(ctx, svcCtx)
_, _ = logic.CreateUser(req)
}
}

332
backend/internal/logic/user/deleteuserlogic_test.go

@ -0,0 +1,332 @@
package user
import (
"context"
"testing"
"time"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.DeleteUserRequest{}
// TestDeleteUser_Success 测试成功删除用户
func TestDeleteUser_Success(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
userId := user.Id
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewDeleteUserLogic(ctx, svcCtx)
// 准备删除请求
req := &types.DeleteUserRequest{
Id: userId,
}
// 执行测试
resp, err := logic.DeleteUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 200, resp.Code)
assert.Equal(t, "删除成功", resp.Message)
assert.True(t, resp.Success)
// 验证用户已被删除
_, err = model.FindOne(context.Background(), svcCtx.DB, userId)
require.Error(t, err)
assert.Equal(t, model.ErrNotFound, err)
}
// TestDeleteUser_NotFound 测试删除不存在的用户
func TestDeleteUser_NotFound(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewDeleteUserLogic(ctx, svcCtx)
// 使用不存在的用户ID
req := &types.DeleteUserRequest{
Id: 99999,
}
// 执行测试
resp, err := logic.DeleteUser(req)
// 验证结果 - 应该返回响应但用户不存在
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 404, resp.Code)
assert.Equal(t, "用户不存在", resp.Message)
assert.False(t, resp.Success)
}
// TestDeleteUser_NegativeId 测试使用负数ID删除
func TestDeleteUser_NegativeId(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewDeleteUserLogic(ctx, svcCtx)
// 使用负数ID
req := &types.DeleteUserRequest{
Id: -1,
}
// 执行测试
resp, err := logic.DeleteUser(req)
// 验证结果 - 应该返回响应但用户不存在
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 404, resp.Code)
assert.Equal(t, "用户不存在", resp.Message)
}
// TestDeleteUser_ZeroId 测试使用零ID删除
func TestDeleteUser_ZeroId(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewDeleteUserLogic(ctx, svcCtx)
// 使用零ID
req := &types.DeleteUserRequest{
Id: 0,
}
// 执行测试
resp, err := logic.DeleteUser(req)
// 验证结果 - 应该返回响应但用户不存在
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 404, resp.Code)
assert.Equal(t, "用户不存在", resp.Message)
}
// TestDeleteUser_ThenRecreate 测试删除后重建用户
func TestDeleteUser_ThenRecreate(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
oldUserId := user.Id
// 创建上下文
ctx := context.Background()
// 删除用户
logic1 := NewDeleteUserLogic(ctx, svcCtx)
req1 := &types.DeleteUserRequest{
Id: oldUserId,
}
_, err = logic1.DeleteUser(req1)
require.NoError(t, err)
// 重建用户(使用相同的邮箱)
createLogic := NewCreateUserLogic(ctx, svcCtx)
createReq := &types.CreateUserRequest{
Username: "testuser2",
Email: "test@example.com",
Password: "newpassword",
}
newUser, err := createLogic.CreateUser(createReq)
// 验证结果 - 新用户ID应该不同
require.NoError(t, err)
require.NotNil(t, newUser)
assert.NotEqual(t, oldUserId, newUser.Id)
}
// TestDeleteUser_DeletedTwice 测试重复删除同一个用户
func TestDeleteUser_DeletedTwice(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
userId := user.Id
// 创建上下文
ctx := context.Background()
// 第一次删除
logic1 := NewDeleteUserLogic(ctx, svcCtx)
req := &types.DeleteUserRequest{
Id: userId,
}
resp1, err := logic1.DeleteUser(req)
require.NoError(t, err)
assert.Equal(t, 200, resp1.Code)
// 第二次删除(应该返回404)
logic2 := NewDeleteUserLogic(ctx, svcCtx)
resp2, err := logic2.DeleteUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp2)
assert.Equal(t, 404, resp2.Code)
assert.Equal(t, "用户不存在", resp2.Message)
}
// TestDeleteUser_WithProfile 测试删除带个人资料的用户
func TestDeleteUser_WithProfile(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
userId := user.Id
// 创建个人资料
profile := &model.Profile{
UserId: userId,
Avatar: "http://example.com/avatar.jpg",
Bio: "Test bio",
CreatedAt: now,
UpdatedAt: now,
}
err = svcCtx.DB.Create(profile).Error
require.NoError(t, err)
// 删除用户
ctx := context.Background()
logic := NewDeleteUserLogic(ctx, svcCtx)
req := &types.DeleteUserRequest{
Id: userId,
}
resp, err := logic.DeleteUser(req)
// 验证结果
require.NoError(t, err)
assert.Equal(t, 200, resp.Code)
// 验证用户和资料都已删除(或用户被删除后,profile变成孤数据)
_, err = model.FindOne(context.Background(), svcCtx.DB, userId)
assert.Error(t, err)
assert.Equal(t, model.ErrNotFound, err)
}
// BenchmarkDeleteUser 性能测试
func BenchmarkDeleteUser(b *testing.B) {
svcCtx, _ := setupUserTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// 为每次迭代创建用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
if err != nil {
b.Fatal(err)
}
logic := NewDeleteUserLogic(ctx, svcCtx)
req := &types.DeleteUserRequest{
Id: user.Id,
}
_, _ = logic.DeleteUser(req)
}
}

256
backend/internal/logic/user/getuserlogic_test.go

@ -0,0 +1,256 @@
package user
import (
"context"
"testing"
"time"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.GetUserRequest{}
// TestGetUser_Success 测试成功获取用户
func TestGetUser_Success(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 准备获取用户请求数据
req := &types.GetUserRequest{
Id: user.Id,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
assert.Equal(t, "13800138000", resp.Phone)
assert.Equal(t, 1, resp.Status)
}
// TestGetUser_NotFound 测试用户不存在
func TestGetUser_NotFound(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 使用不存在的用户ID
req := &types.GetUserRequest{
Id: 99999,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestGetUser_NegativeId 测试使用负数ID
func TestGetUser_NegativeId(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 使用负数ID
req := &types.GetUserRequest{
Id: -1,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestGetUser_ZeroId 测试使用零ID
func TestGetUser_ZeroId(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 使用零ID
req := &types.GetUserRequest{
Id: 0,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestGetUser_UserWithEmptyPhone 测试获取没有手机号的用户
func TestGetUser_UserWithEmptyPhone(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户,手机号为空
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 准备获取用户请求数据
req := &types.GetUserRequest{
Id: user.Id,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "", resp.Phone)
}
// TestGetUser_DisabledUser 测试获取禁用状态的用户
func TestGetUser_DisabledUser(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户,状态为禁用
now := time.Now()
user := &model.User{
Username: "disableduser",
Email: "disabled@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 2, // 禁用状态
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewGetUserLogic(ctx, svcCtx)
// 准备获取用户请求数据
req := &types.GetUserRequest{
Id: user.Id,
}
// 执行测试
resp, err := logic.GetUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 2, resp.Status)
}
// BenchmarkGetUser 性能测试
func BenchmarkGetUser(b *testing.B) {
svcCtx, _ := setupUserTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
if err != nil {
b.Fatal(err)
}
ctx := context.Background()
req := &types.GetUserRequest{
Id: user.Id,
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
logic := NewGetUserLogic(ctx, svcCtx)
_, _ = logic.GetUser(req)
}
}

405
backend/internal/logic/user/updateuserlogic_test.go

@ -0,0 +1,405 @@
package user
import (
"context"
"testing"
"time"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.UpdateUserRequest{}
// TestUpdateUser_Success 测试成功更新用户
func TestUpdateUser_Success(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "olduser",
Email: "old@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateUserRequest{
Id: user.Id,
Username: "newuser",
Email: "new@example.com",
Phone: "13900139000",
Status: 2,
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "newuser", resp.Username)
assert.Equal(t, "new@example.com", resp.Email)
assert.Equal(t, "13900139000", resp.Phone)
assert.Equal(t, 2, resp.Status)
}
// TestUpdateUser_OnlyUsername 测试只更新用户名
func TestUpdateUser_OnlyUsername(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "olduser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 准备更新请求数据 - 只更新用户名
req := &types.UpdateUserRequest{
Id: user.Id,
Username: "newuser",
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "newuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email) // 保持不变
assert.Equal(t, "13800138000", resp.Phone) // 保持不变
}
// TestUpdateUser_OnlyEmail 测试只更新邮箱
func TestUpdateUser_OnlyEmail(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "old@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 准备更新请求数据 - 只更新邮箱
req := &types.UpdateUserRequest{
Id: user.Id,
Email: "new@example.com",
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "new@example.com", resp.Email)
assert.Equal(t, "testuser", resp.Username) // 保持不变
}
// TestUpdateUser_DuplicateEmail 测试使用重复的邮箱
func TestUpdateUser_DuplicateEmail(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建两个测试用户
now := time.Now()
user1 := &model.User{
Username: "user1",
Email: "user1@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
user2 := &model.User{
Username: "user2",
Email: "user2@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user1).Error
require.NoError(t, err)
err = svcCtx.DB.Create(user2).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 尝试将 user1 的邮箱更新为 user2 的邮箱
req := &types.UpdateUserRequest{
Id: user1.Id,
Email: "user2@example.com",
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "邮箱已被使用")
}
// TestUpdateUser_SameEmail 测试使用相同的邮箱(应该允许)
func TestUpdateUser_SameEmail(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 使用相同的邮箱
req := &types.UpdateUserRequest{
Id: user.Id,
Email: "test@example.com",
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果 - 应该成功
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "test@example.com", resp.Email)
}
// TestUpdateUser_NotFound 测试更新不存在的用户
func TestUpdateUser_NotFound(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 使用不存在的用户ID
req := &types.UpdateUserRequest{
Id: 99999,
Username: "newuser",
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果 - 应该返回错误
require.Error(t, err)
require.Nil(t, resp)
assert.Contains(t, err.Error(), "用户不存在")
}
// TestUpdateUser_EmptyRequest 测试空请求
func TestUpdateUser_EmptyRequest(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 空请求
req := &types.UpdateUserRequest{
Id: user.Id,
}
// 执行测试 - 应该成功,只是不做任何更新
resp, err := logic.UpdateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
}
// TestUpdateUser_ChangeStatus 测试修改用户状态
func TestUpdateUser_ChangeStatus(t *testing.T) {
svcCtx, cleanup := setupUserTestDB(t)
defer cleanup()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
require.NoError(t, err)
// 创建上下文
ctx := context.Background()
// 创建 Logic 实例
logic := NewUpdateUserLogic(ctx, svcCtx)
// 禁用用户
req := &types.UpdateUserRequest{
Id: user.Id,
Status: 2,
}
// 执行测试
resp, err := logic.UpdateUser(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 2, resp.Status)
// 再次启用用户
logic2 := NewUpdateUserLogic(ctx, svcCtx)
req2 := &types.UpdateUserRequest{
Id: user.Id,
Status: 1,
}
resp2, err := logic2.UpdateUser(req2)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp2)
assert.Equal(t, 1, resp2.Status)
}
// BenchmarkUpdateUser 性能测试
func BenchmarkUpdateUser(b *testing.B) {
svcCtx, _ := setupUserTestDB(&testing.T{})
defer func() {
sqlDB, _ := svcCtx.DB.DB()
if sqlDB != nil {
sqlDB.Close()
}
}()
// 创建测试用户
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := svcCtx.DB.Create(user).Error
if err != nil {
b.Fatal(err)
}
ctx := context.Background()
req := &types.UpdateUserRequest{
Id: user.Id,
Username: "newuser",
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
logic := NewUpdateUserLogic(ctx, svcCtx)
_, _ = logic.UpdateUser(req)
}
}

408
backend/model/profile_model_test.go

@ -0,0 +1,408 @@
package model
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// setupProfileModelTestDB 创建测试数据库(使用MySQL)
func setupProfileModelTestDB(t *testing.T) *gorm.DB {
t.Helper()
// 使用 MySQL 进行测试
dsn := "root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
require.NoError(t, err)
err = db.AutoMigrate(&User{}, &Profile{})
require.NoError(t, err)
// 清理所有测试数据(TRUNCATE)
db.Exec("SET FOREIGN_KEY_CHECKS = 0")
db.Exec("TRUNCATE TABLE profile")
db.Exec("TRUNCATE TABLE user")
db.Exec("SET FOREIGN_KEY_CHECKS = 1")
return db
}
// createTestUserProfile 创建测试用户和关联的个人资料
func createTestUserProfile(t *testing.T, db *gorm.DB) (int64, int64) {
t.Helper()
now := time.Now()
// 创建用户
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
// 创建个人资料
profile := &Profile{
UserId: user.Id,
Avatar: "http://example.com/avatar.jpg",
Bio: "Test bio",
CreatedAt: now,
UpdatedAt: now,
}
err = db.Create(profile).Error
require.NoError(t, err)
return user.Id, profile.Id
}
// TestInsertProfile 测试插入个人资料
func TestInsertProfile(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
// 先创建用户
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
// 创建个人资料
profile := &Profile{
UserId: user.Id,
Avatar: "http://example.com/avatar.jpg",
Bio: "Test bio",
}
id, err := InsertProfile(ctx, db, profile)
require.NoError(t, err)
assert.Greater(t, id, int64(0))
assert.Equal(t, id, profile.Id)
// 验证数据已保存
saved, err := FindOneProfile(ctx, db, id)
require.NoError(t, err)
assert.Equal(t, "http://example.com/avatar.jpg", saved.Avatar)
assert.Equal(t, "Test bio", saved.Bio)
}
// TestFindOneProfile 测试根据ID查询个人资料
func TestFindOneProfile(t *testing.T) {
db := setupProfileModelTestDB(t)
_, profileId := createTestUserProfile(t, db)
ctx := context.Background()
// 查询个人资料
found, err := FindOneProfile(ctx, db, profileId)
require.NoError(t, err)
require.NotNil(t, found)
assert.Equal(t, profileId, found.Id)
assert.Equal(t, "http://example.com/avatar.jpg", found.Avatar)
assert.Equal(t, "Test bio", found.Bio)
}
// TestFindOneProfile_NotFound 测试查询不存在的个人资料
func TestFindOneProfile_NotFound(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
found, err := FindOneProfile(ctx, db, 99999)
require.Error(t, err)
require.Nil(t, found)
assert.Equal(t, ErrNotFound, err)
}
// TestFindProfileByUserId 测试根据用户ID查询个人资料
func TestFindProfileByUserId(t *testing.T) {
db := setupProfileModelTestDB(t)
userId, _ := createTestUserProfile(t, db)
ctx := context.Background()
// 根据用户ID查询
found, err := FindProfileByUserId(ctx, db, userId)
require.NoError(t, err)
require.NotNil(t, found)
assert.Equal(t, userId, found.UserId)
assert.Equal(t, "http://example.com/avatar.jpg", found.Avatar)
}
// TestFindProfileByUserId_NotFound 测试查询不存在的用户个人资料
func TestFindProfileByUserId_NotFound(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
found, err := FindProfileByUserId(ctx, db, 99999)
require.Error(t, err)
require.Nil(t, found)
assert.Equal(t, ErrNotFound, err)
}
// TestUpdateProfile 测试更新个人资料
func TestUpdateProfile(t *testing.T) {
db := setupProfileModelTestDB(t)
_, profileId := createTestUserProfile(t, db)
ctx := context.Background()
// 获取个人资料
profile, err := FindOneProfile(ctx, db, profileId)
require.NoError(t, err)
// 修改数据
profile.Avatar = "http://example.com/newavatar.jpg"
profile.Bio = "Updated bio"
// 更新
err = UpdateProfile(ctx, db, profile)
require.NoError(t, err)
// 验证更新结果
updated, err := FindOneProfile(ctx, db, profileId)
require.NoError(t, err)
assert.Equal(t, "http://example.com/newavatar.jpg", updated.Avatar)
assert.Equal(t, "Updated bio", updated.Bio)
}
// TestDeleteProfile 测试删除个人资料
func TestDeleteProfile(t *testing.T) {
db := setupProfileModelTestDB(t)
_, profileId := createTestUserProfile(t, db)
ctx := context.Background()
// 删除个人资料
err := DeleteProfile(ctx, db, profileId)
require.NoError(t, err)
// 验证个人资料已被删除
_, err = FindOneProfile(ctx, db, profileId)
require.Error(t, err)
assert.Equal(t, ErrNotFound, err)
}
// TestProfile_UserProfileRelation 测试用户与个人资料的关联
func TestProfile_UserProfileRelation(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
// 创建多个用户
now := time.Now()
users := []User{
{Username: "user1", Email: "user1@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now},
{Username: "user2", Email: "user2@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now},
{Username: "user3", Email: "user3@example.com", Password: "pass", Status: 1, CreatedAt: now, UpdatedAt: now},
}
for _, u := range users {
err := db.Create(&u).Error
require.NoError(t, err)
}
// 只为 user1 和 user2 创建个人资料
profile1 := &Profile{UserId: users[0].Id, Avatar: "avatar1.jpg", Bio: "Bio 1", CreatedAt: now, UpdatedAt: now}
profile2 := &Profile{UserId: users[1].Id, Avatar: "avatar2.jpg", Bio: "Bio 2", CreatedAt: now, UpdatedAt: now}
err := db.Create(profile1).Error
require.NoError(t, err)
err = db.Create(profile2).Error
require.NoError(t, err)
// 验证 user1 有个人资料
found1, err := FindProfileByUserId(ctx, db, users[0].Id)
require.NoError(t, err)
assert.Equal(t, "avatar1.jpg", found1.Avatar)
// 验证 user2 有个人资料
found2, err := FindProfileByUserId(ctx, db, users[1].Id)
require.NoError(t, err)
assert.Equal(t, "avatar2.jpg", found2.Avatar)
// 验证 user3 没有个人资料
found3, err := FindProfileByUserId(ctx, db, users[2].Id)
require.Error(t, err)
require.Nil(t, found3)
}
// TestProfile_UniqueUserId 测试用户ID唯一性约束
func TestProfile_UniqueUserId(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
// 创建用户
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
// 创建第一个个人资料
profile1 := &Profile{
UserId: user.Id,
Avatar: "avatar1.jpg",
Bio: "Bio 1",
}
id, err := InsertProfile(ctx, db, profile1)
require.NoError(t, err)
assert.Greater(t, id, int64(0))
// 尝试为同一用户创建第二个个人资料(应该失败)
profile2 := &Profile{
UserId: user.Id,
Avatar: "avatar2.jpg",
Bio: "Bio 2",
}
_, err = InsertProfile(ctx, db, profile2)
// 验证结果 - 应该返回错误(违反唯一性约束)
require.Error(t, err)
}
// TestProfile_EmptyFields 测试空字段
func TestProfile_EmptyFields(t *testing.T) {
db := setupProfileModelTestDB(t)
ctx := context.Background()
// 创建用户
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
// 创建空个人资料
profile := &Profile{
UserId: user.Id,
Avatar: "",
Bio: "",
}
id, err := InsertProfile(ctx, db, profile)
require.NoError(t, err)
assert.Greater(t, id, int64(0))
// 验证保存成功
saved, err := FindOneProfile(ctx, db, id)
require.NoError(t, err)
assert.Equal(t, "", saved.Avatar)
assert.Equal(t, "", saved.Bio)
}
// BenchmarkInsertProfile 性能测试
func BenchmarkInsertProfile(b *testing.B) {
db := setupProfileModelTestDB(&testing.T{})
ctx := context.Background()
// 创建测试用户
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
profile := &Profile{
UserId: user.Id,
Avatar: "http://example.com/avatar.jpg",
Bio: "Test bio",
}
_, _ = InsertProfile(ctx, db, profile)
}
}
// BenchmarkFindProfileByUserId 性能测试
func BenchmarkFindProfileByUserId(b *testing.B) {
db := setupProfileModelTestDB(&testing.T{})
ctx := context.Background()
// 创建测试用户和个人资料
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
if err != nil {
b.Fatal(err)
}
profile := &Profile{
UserId: user.Id,
Avatar: "http://example.com/avatar.jpg",
Bio: "Test bio",
CreatedAt: now,
UpdatedAt: now,
}
err = db.Create(profile).Error
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = FindProfileByUserId(ctx, db, user.Id)
}
}

368
backend/model/user_model_test.go

@ -0,0 +1,368 @@
package model
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// setupUserModelTestDB 创建测试数据库(使用MySQL)
func setupUserModelTestDB(t *testing.T) *gorm.DB {
t.Helper()
// 使用 MySQL 进行测试
dsn := "root:dev123456@tcp(219.159.132.177:17173)/base?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
require.NoError(t, err)
err = db.AutoMigrate(&User{}, &Profile{})
require.NoError(t, err)
// 清理所有测试数据(TRUNCATE)
db.Exec("SET FOREIGN_KEY_CHECKS = 0")
db.Exec("TRUNCATE TABLE profile")
db.Exec("TRUNCATE TABLE user")
db.Exec("SET FOREIGN_KEY_CHECKS = 1")
return db
}
// createTestUser 创建测试用户
func createTestUserModel(t *testing.T, db *gorm.DB) *User {
t.Helper()
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// TestInsertUser 测试插入用户
func TestInsertUser(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
user := &User{
Username: "newuser",
Email: "new@example.com",
Password: "encryptedpassword",
Phone: "13900139000",
Status: 1,
}
id, err := Insert(ctx, db, user)
require.NoError(t, err)
assert.Greater(t, id, int64(0))
assert.Equal(t, id, user.Id)
// 验证数据已保存
saved, err := FindOne(ctx, db, id)
require.NoError(t, err)
assert.Equal(t, "newuser", saved.Username)
assert.Equal(t, "new@example.com", saved.Email)
}
// TestFindOneUser 测试根据ID查询用户
func TestFindOneUser(t *testing.T) {
db := setupUserModelTestDB(t)
// 创建测试用户
user := createTestUserModel(t, db)
ctx := context.Background()
// 查询用户
found, err := FindOne(ctx, db, user.Id)
require.NoError(t, err)
require.NotNil(t, found)
assert.Equal(t, user.Id, found.Id)
assert.Equal(t, "testuser", found.Username)
assert.Equal(t, "test@example.com", found.Email)
}
// TestFindOneUser_NotFound 测试查询不存在的用户
func TestFindOneUser_NotFound(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
found, err := FindOne(ctx, db, 99999)
require.Error(t, err)
require.Nil(t, found)
assert.Equal(t, ErrNotFound, err)
}
// TestFindOneByEmail 测试根据邮箱查询用户
func TestFindOneByEmail(t *testing.T) {
db := setupUserModelTestDB(t)
// 创建测试用户
user := createTestUserModel(t, db)
ctx := context.Background()
// 根据邮箱查询
found, err := FindOneByEmail(ctx, db, "test@example.com")
require.NoError(t, err)
require.NotNil(t, found)
assert.Equal(t, user.Id, found.Id)
assert.Equal(t, "testuser", found.Username)
}
// TestFindOneByEmail_NotFound 测试查询不存在的邮箱
func TestFindOneByEmail_NotFound(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
found, err := FindOneByEmail(ctx, db, "nonexistent@example.com")
require.Error(t, err)
require.Nil(t, found)
assert.Equal(t, ErrNotFound, err)
}
// TestUpdateUser 测试更新用户
func TestUpdateUser(t *testing.T) {
db := setupUserModelTestDB(t)
// 创建测试用户
user := createTestUserModel(t, db)
ctx := context.Background()
// 修改用户数据
user.Username = "updateduser"
user.Phone = "13900139000"
// 更新
err := Update(ctx, db, user)
require.NoError(t, err)
// 验证更新结果
updated, err := FindOne(ctx, db, user.Id)
require.NoError(t, err)
assert.Equal(t, "updateduser", updated.Username)
assert.Equal(t, "13900139000", updated.Phone)
}
// TestDeleteUser 测试删除用户
func TestDeleteUser(t *testing.T) {
db := setupUserModelTestDB(t)
// 创建测试用户
user := createTestUserModel(t, db)
userId := user.Id
ctx := context.Background()
// 删除用户
err := Delete(ctx, db, userId)
require.NoError(t, err)
// 验证用户已被删除
_, err = FindOne(ctx, db, userId)
require.Error(t, err)
assert.Equal(t, ErrNotFound, err)
}
// TestFindList 测试查询用户列表
func TestFindList(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
// 创建多个测试用户
now := time.Now()
users := []User{
{Username: "user1", Email: "user1@example.com", Password: "pass", Phone: "13800000001", Status: 1, CreatedAt: now, UpdatedAt: now},
{Username: "user2", Email: "user2@example.com", Password: "pass", Phone: "13800000002", Status: 1, CreatedAt: now, UpdatedAt: now},
{Username: "user3", Email: "user3@example.com", Password: "pass", Phone: "13800000003", Status: 2, CreatedAt: now, UpdatedAt: now},
{Username: "user4", Email: "user4@example.com", Password: "pass", Phone: "13800000004", Status: 2, CreatedAt: now, UpdatedAt: now},
{Username: "user5", Email: "user5@example.com", Password: "pass", Phone: "13800000005", Status: 1, CreatedAt: now, UpdatedAt: now},
}
for _, u := range users {
err := db.Create(&u).Error
require.NoError(t, err)
}
// 测试分页查询
list, total, err := FindList(ctx, db, 1, 2, "", 0)
require.NoError(t, err)
assert.Equal(t, int64(5), total)
assert.Len(t, list, 2)
// 测试状态筛选
list, total, err = FindList(ctx, db, 1, 10, "", 1)
require.NoError(t, err)
assert.Equal(t, int64(3), total)
// 测试关键词搜索
list, total, err = FindList(ctx, db, 1, 10, "user1", 0)
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Equal(t, "user1", list[0].Username)
// 测试第二页
list, total, err = FindList(ctx, db, 2, 2, "", 0)
require.NoError(t, err)
assert.Equal(t, int64(5), total)
assert.Len(t, list, 2)
assert.Equal(t, "user3", list[0].Username)
}
// TestFindList_EmptyResult 测试查询空列表
func TestFindList_EmptyResult(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
list, total, err := FindList(ctx, db, 1, 10, "", 0)
require.NoError(t, err)
assert.Equal(t, int64(0), total)
assert.Empty(t, list)
}
// TestFindList_WithPhoneSearch 测试通过手机号搜索
func TestFindList_WithPhoneSearch(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
// 创建测试用户
now := time.Now()
user1 := &User{Username: "user1", Email: "user1@example.com", Password: "pass", Phone: "13812345678", Status: 1, CreatedAt: now, UpdatedAt: now}
user2 := &User{Username: "user2", Email: "user2@example.com", Password: "pass", Phone: "13887654321", Status: 1, CreatedAt: now, UpdatedAt: now}
err := db.Create(user1).Error
require.NoError(t, err)
err = db.Create(user2).Error
require.NoError(t, err)
// 搜索手机号
list, total, err := FindList(ctx, db, 1, 10, "138123", 0)
require.NoError(t, err)
assert.Equal(t, int64(1), total)
assert.Equal(t, "user1", list[0].Username)
}
// TestFindList_WithEmailSearch 测试通过邮箱搜索
func TestFindList_WithEmailSearch(t *testing.T) {
db := setupUserModelTestDB(t)
ctx := context.Background()
// 创建测试用户
now := time.Now()
user1 := &User{Username: "user1", Email: "test@company.com", Password: "pass", Phone: "13800000001", Status: 1, CreatedAt: now, UpdatedAt: now}
user2 := &User{Username: "user2", Email: "other@company.com", Password: "pass", Phone: "13800000002", Status: 1, CreatedAt: now, UpdatedAt: now}
err := db.Create(user1).Error
require.NoError(t, err)
err = db.Create(user2).Error
require.NoError(t, err)
// 搜索邮箱
_, total, err := FindList(ctx, db, 1, 10, "@company.com", 0)
require.NoError(t, err)
assert.Equal(t, int64(2), total)
}
// BenchmarkInsertUser 性能测试
func BenchmarkInsertUser(b *testing.B) {
db := setupUserModelTestDB(&testing.T{})
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
_, _ = Insert(ctx, db, user)
}
}
// BenchmarkFindOneUser 性能测试
func BenchmarkFindOneUser(b *testing.B) {
db := setupUserModelTestDB(&testing.T{})
ctx := context.Background()
// 创建测试用户
now := time.Now()
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = FindOne(ctx, db, user.Id)
}
}
// BenchmarkFindList 性能测试
func BenchmarkFindList(b *testing.B) {
db := setupUserModelTestDB(&testing.T{})
ctx := context.Background()
// 创建100个测试用户
now := time.Now()
for i := 0; i < 100; i++ {
user := &User{
Username: "testuser",
Email: "test@example.com",
Password: "encryptedpassword",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
if err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = FindList(ctx, db, 1, 10, "", 0)
}
}

166
backend/run_logic_tests.sh

@ -0,0 +1,166 @@
package profile
import (
"context"
"testing"
"time"
"github.com/youruser/base/internal/svc"
"github.com/youruser/base/internal/types"
"github.com/youruser/base/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Ensure imports are used
var _ = svc.ServiceContext{}
var _ = types.GetProfileResponse{}
// setupTestDB 创建测试数据库并返回 ServiceContext(使用MySQL)
func setupTestDB(t *testing.T) (*svc.ServiceContext, func()) {
t.Helper()
// 使用 MySQL 进行测试
dsn := "root:root@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=true&loc=Local"
db, err := gorm.Open(dsn, &gorm.Config{})
require.NoError(t, err)
// 自动迁移表
err = db.AutoMigrate(&model.User{}, &model.Profile{})
require.NoError(t, err)
// 创建测试 ServiceContext
svcCtx := &svc.ServiceContext{
DB: db,
}
// 清理函数
cleanup := func() {
sqlDB, _ := db.DB()
if sqlDB != nil {
sqlDB.Close()
}
}
return svcCtx, cleanup
}
// createTestUser 创建测试用户
func createTestUser(t *testing.T, db *gorm.DB) *model.User {
t.Helper()
now := time.Now()
user := &model.User{
Username: "testuser",
Email: "test@example.com",
Password: "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
Phone: "13800138000",
Status: 1,
CreatedAt: now,
UpdatedAt: now,
}
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// TestGetProfile_Success 测试成功获取个人信息
func TestGetProfile_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
createTestProfile(t, svcCtx.DB, user.Id)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewGetProfileLogic(ctx, svcCtx)
// 执行测试
resp, err := logic.GetProfile()
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "testuser", resp.Username)
assert.Equal(t, "test@example.com", resp.Email)
assert.Equal(t, "13800138000", resp.Phone)
assert.Equal(t, "http://example.com/avatar.jpg", resp.Avatar)
assert.Equal(t, "This is a test bio", resp.Bio)
assert.Equal(t, 1, resp.Status)
}
// TestUpdateProfile_Success 测试成功更新个人资料
func TestUpdateProfile_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewUpdateProfileLogic(ctx, svcCtx)
// 准备更新请求数据
req := &types.UpdateProfileRequest{
Username: "updateduser",
Phone: "13900139000",
Avatar: "http://example.com/newavatar.jpg",
Bio: "Updated bio text",
}
// 执行测试
resp, err := logic.UpdateProfile(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, user.Id, resp.Id)
assert.Equal(t, "updateduser", resp.Username)
assert.Equal(t, "13900139000", resp.Phone)
assert.Equal(t, "http://example.com/newavatar.jpg", resp.Avatar)
assert.Equal(t, "Updated bio text", resp.Bio)
}
// TestChangePassword_Success 测试成功修改密码
func TestChangePassword_Success(t *testing.T) {
svcCtx, cleanup := setupTestDB(t)
defer cleanup()
// 创建测试用户,密码为 "password"
user := createTestUser(t, svcCtx.DB)
// 设置上下文,包含用户ID
ctx := context.WithValue(context.Background(), "userId", user.Id)
// 创建 Logic 实例
logic := NewChangePasswordLogic(ctx, svcCtx)
// 准备修改密码请求数据
req := &types.ChangePasswordRequest{
OldPassword: "password",
NewPassword: "newpassword123",
}
// 执行测试
resp, err := logic.ChangePassword(req)
// 验证结果
require.NoError(t, err)
require.NotNil(t, resp)
assert.Equal(t, 200, resp.Code)
assert.Equal(t, "修改密码成功", resp.Message)
}
}

4
backend/run_tests.bat

@ -0,0 +1,4 @@
@echo off
set CGO_ENABLED=1
go test -v ./internal/logic/... %*
pause

2
backend/tests/user/test_user.sh

@ -101,7 +101,7 @@ log_success "Verify success"
log_step "5" "List query"
LIST_RESULT=$(curl -s -X GET "${BASE_URL}/${MODULE}s?page=1&pageSize=10" -H "Authorization: Bearer ${AUTH_TOKEN}")
if echo "$LIST_RESULT" | grep -q '"id"'; then
if echo "$LIST_RESULT" | grep -q "\"id\"; then
log_success "List success"
else
log_error "List failed"

Loading…
Cancel
Save