package unit import ( "testing" "time" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/go-playground/validator/v10" "github.com/stretchr/testify/assert" ) // TestUserValidation 测试用户模型验证 func TestUserValidation(t *testing.T) { validate := validator.New() t.Run("有效的创建用户请求", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "validuser", Email: "valid@example.com", Password: "password123", } err := validate.Struct(req) assert.NoError(t, err) }) t.Run("用户名太短", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "ab", // 少于 3 个字符 Email: "valid@example.com", Password: "password123", } err := validate.Struct(req) assert.Error(t, err) }) t.Run("用户名太长", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "a123456789012345678901234567890123456789012345678901", // 超过 50 个字符 Email: "valid@example.com", Password: "password123", } err := validate.Struct(req) assert.Error(t, err) }) t.Run("无效的邮箱格式", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "validuser", Email: "invalid-email", Password: "password123", } err := validate.Struct(req) assert.Error(t, err) }) t.Run("密码太短", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "validuser", Email: "valid@example.com", Password: "short", // 少于 8 个字符 } err := validate.Struct(req) assert.Error(t, err) }) t.Run("缺少必填字段", func(t *testing.T) { req := &model.CreateUserRequest{ Username: "validuser", // 缺少 Email 和 Password } err := validate.Struct(req) assert.Error(t, err) }) } // TestUserUpdateValidation 测试用户更新验证 func TestUserUpdateValidation(t *testing.T) { validate := validator.New() t.Run("有效的更新请求", func(t *testing.T) { email := "newemail@example.com" status := constants.UserStatusActive req := &model.UpdateUserRequest{ Email: &email, Status: &status, } err := validate.Struct(req) assert.NoError(t, err) }) t.Run("无效的邮箱格式", func(t *testing.T) { email := "invalid-email" req := &model.UpdateUserRequest{ Email: &email, } err := validate.Struct(req) assert.Error(t, err) }) t.Run("无效的状态值", func(t *testing.T) { status := "invalid_status" req := &model.UpdateUserRequest{ Status: &status, } err := validate.Struct(req) assert.Error(t, err) }) t.Run("空更新请求", func(t *testing.T) { req := &model.UpdateUserRequest{} err := validate.Struct(req) assert.NoError(t, err) // 空更新请求应该是有效的 }) } // TestOrderValidation 测试订单模型验证 func TestOrderValidation(t *testing.T) { validate := validator.New() t.Run("有效的创建订单请求", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-2025-001", UserID: 1, Amount: 10000, Remark: "测试订单", } err := validate.Struct(req) assert.NoError(t, err) }) t.Run("订单号太短", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-123", // 少于 10 个字符 UserID: 1, Amount: 10000, } err := validate.Struct(req) assert.Error(t, err) }) t.Run("订单号太长", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-12345678901234567890123456789012345678901234567890", // 超过 50 个字符 UserID: 1, Amount: 10000, } err := validate.Struct(req) assert.Error(t, err) }) t.Run("用户ID无效", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-2025-001", UserID: 0, // 用户ID必须大于0 Amount: 10000, } err := validate.Struct(req) assert.Error(t, err) }) t.Run("金额为负数", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-2025-001", UserID: 1, Amount: -1000, // 金额不能为负数 } err := validate.Struct(req) assert.Error(t, err) }) t.Run("缺少必填字段", func(t *testing.T) { req := &model.CreateOrderRequest{ OrderID: "ORD-2025-001", // 缺少 UserID 和 Amount } err := validate.Struct(req) assert.Error(t, err) }) } // TestOrderUpdateValidation 测试订单更新验证 func TestOrderUpdateValidation(t *testing.T) { validate := validator.New() t.Run("有效的更新请求", func(t *testing.T) { status := constants.OrderStatusPaid remark := "已支付" req := &model.UpdateOrderRequest{ Status: &status, Remark: &remark, } err := validate.Struct(req) assert.NoError(t, err) }) t.Run("无效的状态值", func(t *testing.T) { status := "invalid_status" req := &model.UpdateOrderRequest{ Status: &status, } err := validate.Struct(req) assert.Error(t, err) }) } // TestUserModel 测试用户模型 func TestUserModel(t *testing.T) { t.Run("创建用户模型", func(t *testing.T) { user := &model.User{ Username: "testuser", Email: "test@example.com", Password: "hashedpassword", Status: constants.UserStatusActive, } assert.Equal(t, "testuser", user.Username) assert.Equal(t, "test@example.com", user.Email) assert.Equal(t, constants.UserStatusActive, user.Status) }) t.Run("用户表名", func(t *testing.T) { user := &model.User{} assert.Equal(t, "tb_user", user.TableName()) }) t.Run("软删除字段", func(t *testing.T) { user := &model.User{ Username: "testuser", Email: "test@example.com", Password: "hashedpassword", Status: constants.UserStatusActive, } // DeletedAt 应该是 nil (未删除) assert.True(t, user.DeletedAt.Time.IsZero()) }) t.Run("LastLoginAt 可选字段", func(t *testing.T) { user := &model.User{ Username: "testuser", Email: "test@example.com", Password: "hashedpassword", Status: constants.UserStatusActive, } assert.Nil(t, user.LastLoginAt) // 设置登录时间 now := time.Now() user.LastLoginAt = &now assert.NotNil(t, user.LastLoginAt) assert.Equal(t, now, *user.LastLoginAt) }) } // TestOrderModel 测试订单模型 func TestOrderModel(t *testing.T) { t.Run("创建订单模型", func(t *testing.T) { order := &model.Order{ OrderID: "ORD-2025-001", UserID: 1, Amount: 10000, Status: constants.OrderStatusPending, Remark: "测试订单", } assert.Equal(t, "ORD-2025-001", order.OrderID) assert.Equal(t, uint(1), order.UserID) assert.Equal(t, int64(10000), order.Amount) assert.Equal(t, constants.OrderStatusPending, order.Status) }) t.Run("订单表名", func(t *testing.T) { order := &model.Order{} assert.Equal(t, "tb_order", order.TableName()) }) t.Run("可选时间字段", func(t *testing.T) { order := &model.Order{ OrderID: "ORD-2025-001", UserID: 1, Amount: 10000, Status: constants.OrderStatusPending, } assert.Nil(t, order.PaidAt) assert.Nil(t, order.CompletedAt) // 设置支付时间 now := time.Now() order.PaidAt = &now assert.NotNil(t, order.PaidAt) assert.Equal(t, now, *order.PaidAt) }) } // TestBaseModel 测试基础模型 func TestBaseModel(t *testing.T) { t.Run("BaseModel 字段", func(t *testing.T) { user := &model.User{ Username: "testuser", Email: "test@example.com", Password: "hashedpassword", Status: constants.UserStatusActive, } // ID 应该是 0 (未保存) assert.Zero(t, user.ID) // 时间戳应该是零值 assert.True(t, user.CreatedAt.IsZero()) assert.True(t, user.UpdatedAt.IsZero()) }) } // TestUserStatusConstants 测试用户状态常量 func TestUserStatusConstants(t *testing.T) { t.Run("用户状态常量定义", func(t *testing.T) { assert.Equal(t, "active", constants.UserStatusActive) assert.Equal(t, "inactive", constants.UserStatusInactive) assert.Equal(t, "suspended", constants.UserStatusSuspended) }) t.Run("用户状态验证", func(t *testing.T) { validStatuses := []string{ constants.UserStatusActive, constants.UserStatusInactive, constants.UserStatusSuspended, } for _, status := range validStatuses { user := &model.User{ Username: "testuser", Email: "test@example.com", Password: "hashedpassword", Status: status, } assert.Contains(t, validStatuses, user.Status) } }) } // TestOrderStatusConstants 测试订单状态常量 func TestOrderStatusConstants(t *testing.T) { t.Run("订单状态常量定义", func(t *testing.T) { assert.Equal(t, "pending", constants.OrderStatusPending) assert.Equal(t, "paid", constants.OrderStatusPaid) assert.Equal(t, "processing", constants.OrderStatusProcessing) assert.Equal(t, "completed", constants.OrderStatusCompleted) assert.Equal(t, "cancelled", constants.OrderStatusCancelled) }) t.Run("订单状态流转", func(t *testing.T) { order := &model.Order{ OrderID: "ORD-2025-001", UserID: 1, Amount: 10000, Status: constants.OrderStatusPending, } // 订单状态流转:pending -> paid -> processing -> completed assert.Equal(t, constants.OrderStatusPending, order.Status) order.Status = constants.OrderStatusPaid assert.Equal(t, constants.OrderStatusPaid, order.Status) order.Status = constants.OrderStatusProcessing assert.Equal(t, constants.OrderStatusProcessing, order.Status) order.Status = constants.OrderStatusCompleted assert.Equal(t, constants.OrderStatusCompleted, order.Status) }) t.Run("订单取消", func(t *testing.T) { order := &model.Order{ OrderID: "ORD-2025-002", UserID: 1, Amount: 10000, Status: constants.OrderStatusPending, } // 从任何状态都可以取消 order.Status = constants.OrderStatusCancelled assert.Equal(t, constants.OrderStatusCancelled, order.Status) }) } // TestUserResponse 测试用户响应模型 func TestUserResponse(t *testing.T) { t.Run("创建用户响应", func(t *testing.T) { now := time.Now() resp := &model.UserResponse{ ID: 1, Username: "testuser", Email: "test@example.com", Status: constants.UserStatusActive, CreatedAt: now, UpdatedAt: now, } assert.Equal(t, uint(1), resp.ID) assert.Equal(t, "testuser", resp.Username) assert.Equal(t, "test@example.com", resp.Email) assert.Equal(t, constants.UserStatusActive, resp.Status) }) t.Run("用户响应不包含密码", func(t *testing.T) { // UserResponse 结构体不应该包含 Password 字段 resp := &model.UserResponse{ ID: 1, Username: "testuser", Email: "test@example.com", Status: constants.UserStatusActive, } // 验证结构体大小合理 (不包含密码字段) assert.NotNil(t, resp) }) } // TestListResponse 测试列表响应模型 func TestListResponse(t *testing.T) { t.Run("用户列表响应", func(t *testing.T) { users := []model.UserResponse{ {ID: 1, Username: "user1", Email: "user1@example.com", Status: constants.UserStatusActive}, {ID: 2, Username: "user2", Email: "user2@example.com", Status: constants.UserStatusActive}, } resp := &model.ListUsersResponse{ Users: users, Page: 1, PageSize: 20, Total: 100, TotalPages: 5, } assert.Equal(t, 2, len(resp.Users)) assert.Equal(t, 1, resp.Page) assert.Equal(t, 20, resp.PageSize) assert.Equal(t, int64(100), resp.Total) assert.Equal(t, 5, resp.TotalPages) }) t.Run("订单列表响应", func(t *testing.T) { orders := []model.OrderResponse{ {ID: 1, OrderID: "ORD-001", UserID: 1, Amount: 10000, Status: constants.OrderStatusPending}, {ID: 2, OrderID: "ORD-002", UserID: 1, Amount: 20000, Status: constants.OrderStatusPaid}, } resp := &model.ListOrdersResponse{ Orders: orders, Page: 1, PageSize: 20, Total: 50, TotalPages: 3, } assert.Equal(t, 2, len(resp.Orders)) assert.Equal(t, 1, resp.Page) assert.Equal(t, 20, resp.PageSize) assert.Equal(t, int64(50), resp.Total) assert.Equal(t, 3, resp.TotalPages) }) } // TestFieldTags 测试字段标签 func TestFieldTags(t *testing.T) { t.Run("User GORM 标签", func(t *testing.T) { user := &model.User{} // 验证 TableName 方法存在 tableName := user.TableName() assert.Equal(t, "tb_user", tableName) }) t.Run("Order GORM 标签", func(t *testing.T) { order := &model.Order{} // 验证 TableName 方法存在 tableName := order.TableName() assert.Equal(t, "tb_order", tableName) }) }