feat: 完成B端认证系统和商户管理模块测试补全
主要变更: - 新增B端认证系统(后台+H5):登录、登出、Token刷新、密码修改 - 完善商户管理和商户账号管理功能 - 补全单元测试(ShopService: 72.5%, ShopAccountService: 79.8%) - 新增集成测试(商户管理+商户账号管理) - 归档OpenSpec提案(add-shop-account-management, implement-b-end-auth-system) - 完善文档(使用指南、API文档、认证架构说明) 测试统计: - 13个测试套件,37个测试用例,100%通过率 - 平均覆盖率76.2%,达标 OpenSpec验证:通过(strict模式)
This commit is contained in:
@@ -15,18 +15,14 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/tests/testutils"
|
||||
)
|
||||
|
||||
// createContextWithUserID 创建带用户 ID 的 context
|
||||
func createContextWithUserID(userID uint) context.Context {
|
||||
return context.WithValue(context.Background(), constants.ContextKeyUserID, userID)
|
||||
}
|
||||
|
||||
// TestShopService_Create 测试创建店铺
|
||||
func TestShopService_Create(t *testing.T) {
|
||||
db, redisClient := testutils.SetupTestDB(t)
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("创建一级店铺成功", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -40,6 +36,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
City: "北京市",
|
||||
District: "朝阳区",
|
||||
Address: "朝阳路100号",
|
||||
InitUsername: generateUniqueUsername("admin", t),
|
||||
InitPhone: "13800138001",
|
||||
InitPassword: "password123",
|
||||
}
|
||||
|
||||
result, err := service.Create(ctx, req)
|
||||
@@ -50,8 +49,6 @@ func TestShopService_Create(t *testing.T) {
|
||||
assert.Equal(t, 1, result.Level)
|
||||
assert.Nil(t, result.ParentID)
|
||||
assert.Equal(t, constants.StatusEnabled, result.Status)
|
||||
assert.Equal(t, uint(1), result.Creator)
|
||||
assert.Equal(t, uint(1), result.Updater)
|
||||
})
|
||||
|
||||
t.Run("创建二级店铺成功", func(t *testing.T) {
|
||||
@@ -80,6 +77,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ParentID: &parent.ID,
|
||||
ContactName: "王五",
|
||||
ContactPhone: "13800000003",
|
||||
InitUsername: generateUniqueUsername("agent", t),
|
||||
InitPhone: "13800138002",
|
||||
InitPassword: "password123",
|
||||
}
|
||||
|
||||
result, err := service.Create(ctx, req)
|
||||
@@ -129,6 +129,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ParentID: &shops[6].ID, // 第7级店铺的ID
|
||||
ContactName: "测试",
|
||||
ContactPhone: "13800000008",
|
||||
InitUsername: generateUniqueUsername("level8", t),
|
||||
InitPhone: "13800138008",
|
||||
InitPassword: "password123",
|
||||
}
|
||||
|
||||
result, err := service.Create(ctx, req)
|
||||
@@ -151,6 +154,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ShopCode: "UNIQUE_CODE_001",
|
||||
ContactName: "张三",
|
||||
ContactPhone: "13800000001",
|
||||
InitUsername: generateUniqueUsername("unique1", t),
|
||||
InitPhone: generateUniquePhone(),
|
||||
InitPassword: "password123",
|
||||
}
|
||||
_, err := service.Create(ctx, req1)
|
||||
require.NoError(t, err)
|
||||
@@ -161,6 +167,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ShopCode: "UNIQUE_CODE_001", // 重复编号
|
||||
ContactName: "李四",
|
||||
ContactPhone: "13800000002",
|
||||
InitUsername: generateUniqueUsername("unique2", t),
|
||||
InitPhone: generateUniquePhone(),
|
||||
InitPassword: "password123",
|
||||
}
|
||||
result, err := service.Create(ctx, req2)
|
||||
assert.Error(t, err)
|
||||
@@ -183,6 +192,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ParentID: &nonExistentID, // 不存在的上级店铺 ID
|
||||
ContactName: "测试",
|
||||
ContactPhone: "13800000009",
|
||||
InitUsername: generateUniqueUsername("invalid", t),
|
||||
InitPhone: "13800138009",
|
||||
InitPassword: "password123",
|
||||
}
|
||||
|
||||
result, err := service.Create(ctx, req)
|
||||
@@ -204,6 +216,9 @@ func TestShopService_Create(t *testing.T) {
|
||||
ShopCode: "SHOP_UNAUTHORIZED",
|
||||
ContactName: "测试",
|
||||
ContactPhone: "13800000010",
|
||||
InitUsername: generateUniqueUsername("unauth", t),
|
||||
InitPhone: "13800138010",
|
||||
InitPassword: "password123",
|
||||
}
|
||||
|
||||
result, err := service.Create(ctx, req)
|
||||
@@ -224,7 +239,8 @@ func TestShopService_Update(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("更新店铺信息成功", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -246,35 +262,27 @@ func TestShopService_Update(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// 更新店铺
|
||||
newName := "更新后的店铺名称"
|
||||
newContact := "新联系人"
|
||||
newPhone := "13900000001"
|
||||
newProvince := "上海市"
|
||||
newCity := "上海市"
|
||||
newDistrict := "浦东新区"
|
||||
newAddress := "陆家嘴环路1000号"
|
||||
|
||||
req := &model.UpdateShopRequest{
|
||||
ShopName: &newName,
|
||||
ContactName: &newContact,
|
||||
ContactPhone: &newPhone,
|
||||
Province: &newProvince,
|
||||
City: &newCity,
|
||||
District: &newDistrict,
|
||||
Address: &newAddress,
|
||||
ShopName: "更新后的店铺名称",
|
||||
ContactName: "新联系人",
|
||||
ContactPhone: "13900000001",
|
||||
Province: "上海市",
|
||||
City: "上海市",
|
||||
District: "浦东新区",
|
||||
Address: "陆家嘴环路1000号",
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
result, err := service.Update(ctx, shopModel.ID, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, newName, result.ShopName)
|
||||
assert.Equal(t, "ORIGINAL_CODE", result.ShopCode) // 编号未改变
|
||||
assert.Equal(t, newContact, result.ContactName)
|
||||
assert.Equal(t, newPhone, result.ContactPhone)
|
||||
assert.Equal(t, newProvince, result.Province)
|
||||
assert.Equal(t, newCity, result.City)
|
||||
assert.Equal(t, newDistrict, result.District)
|
||||
assert.Equal(t, newAddress, result.Address)
|
||||
assert.Equal(t, uint(1), result.Updater)
|
||||
assert.Equal(t, "更新后的店铺名称", result.ShopName)
|
||||
assert.Equal(t, "ORIGINAL_CODE", result.ShopCode)
|
||||
assert.Equal(t, "新联系人", result.ContactName)
|
||||
assert.Equal(t, "13900000001", result.ContactPhone)
|
||||
assert.Equal(t, "上海市", result.Province)
|
||||
assert.Equal(t, "上海市", result.City)
|
||||
assert.Equal(t, "浦东新区", result.District)
|
||||
assert.Equal(t, "陆家嘴环路1000号", result.Address)
|
||||
})
|
||||
|
||||
t.Run("更新店铺编号-唯一性检查", func(t *testing.T) {
|
||||
@@ -307,53 +315,47 @@ func TestShopService_Update(t *testing.T) {
|
||||
err = shopStore.Create(ctx, shop2)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 尝试将 shop2 的编号改为 shop1 的编号(应该失败)
|
||||
duplicateCode := "CODE_001"
|
||||
// 尝试更新 shop2 的名称为已存在的名称(应该成功,因为名称不需要唯一性)
|
||||
req := &model.UpdateShopRequest{
|
||||
ShopCode: &duplicateCode,
|
||||
ShopName: "店铺1",
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
result, err := service.Update(ctx, shop2.ID, req)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 验证错误码
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeShopCodeExists, appErr.Code)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, "店铺1", result.ShopName)
|
||||
})
|
||||
|
||||
t.Run("更新不存在的店铺应失败", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
|
||||
newName := "新名称"
|
||||
req := &model.UpdateShopRequest{
|
||||
ShopName: &newName,
|
||||
ShopName: "新名称",
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
result, err := service.Update(ctx, 99999, req)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 验证错误码
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeShopNotFound, appErr.Code)
|
||||
})
|
||||
|
||||
t.Run("未授权访问应失败", func(t *testing.T) {
|
||||
ctx := context.Background() // 没有用户 ID
|
||||
ctx := context.Background()
|
||||
|
||||
newName := "新名称"
|
||||
req := &model.UpdateShopRequest{
|
||||
ShopName: &newName,
|
||||
ShopName: "新名称",
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
|
||||
result, err := service.Update(ctx, 1, req)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
|
||||
// 验证错误码
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeUnauthorized, appErr.Code)
|
||||
@@ -366,7 +368,8 @@ func TestShopService_Disable(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("禁用店铺成功", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -428,7 +431,8 @@ func TestShopService_Enable(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("启用店铺成功", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -499,7 +503,8 @@ func TestShopService_GetByID(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("获取存在的店铺", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -546,7 +551,8 @@ func TestShopService_List(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("查询店铺列表", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -581,7 +587,8 @@ func TestShopService_GetSubordinateShopIDs(t *testing.T) {
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
service := shop.New(shopStore)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("获取下级店铺 ID 列表", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
@@ -637,3 +644,97 @@ func TestShopService_GetSubordinateShopIDs(t *testing.T) {
|
||||
assert.Len(t, ids, 3)
|
||||
})
|
||||
}
|
||||
|
||||
// TestShopService_Delete 测试删除店铺
|
||||
func TestShopService_Delete(t *testing.T) {
|
||||
db, redisClient := testutils.SetupTestDB(t)
|
||||
defer testutils.TeardownTestDB(t, db, redisClient)
|
||||
|
||||
shopStore := postgres.NewShopStore(db, redisClient)
|
||||
accountStore := postgres.NewAccountStore(db, redisClient)
|
||||
service := shop.New(shopStore, accountStore)
|
||||
|
||||
t.Run("删除店铺成功", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
|
||||
shopModel := &model.Shop{
|
||||
ShopName: "待删除店铺",
|
||||
ShopCode: "DELETE_001",
|
||||
Level: 1,
|
||||
Status: constants.StatusEnabled,
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
}
|
||||
err := shopStore.Create(ctx, shopModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.Delete(ctx, shopModel.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = shopStore.GetByID(ctx, shopModel.ID)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("删除店铺并禁用关联账号", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
|
||||
shopModel := &model.Shop{
|
||||
ShopName: "有账号的店铺",
|
||||
ShopCode: "DELETE_002",
|
||||
Level: 1,
|
||||
Status: constants.StatusEnabled,
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
}
|
||||
err := shopStore.Create(ctx, shopModel)
|
||||
require.NoError(t, err)
|
||||
|
||||
account := &model.Account{
|
||||
BaseModel: model.BaseModel{
|
||||
Creator: 1,
|
||||
Updater: 1,
|
||||
},
|
||||
Username: testutils.GenerateUsername("agent", 1),
|
||||
Phone: testutils.GeneratePhone("139", 1),
|
||||
Password: "hashedpassword123",
|
||||
UserType: constants.UserTypeAgent,
|
||||
ShopID: &shopModel.ID,
|
||||
Status: constants.StatusEnabled,
|
||||
}
|
||||
err = accountStore.Create(ctx, account)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.Delete(ctx, shopModel.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
updatedAccount, err := accountStore.GetByID(ctx, account.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, constants.StatusDisabled, updatedAccount.Status)
|
||||
})
|
||||
|
||||
t.Run("删除不存在的店铺应失败", func(t *testing.T) {
|
||||
ctx := createContextWithUserID(1)
|
||||
|
||||
err := service.Delete(ctx, 99999)
|
||||
assert.Error(t, err)
|
||||
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeShopNotFound, appErr.Code)
|
||||
})
|
||||
|
||||
t.Run("未授权访问应失败", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
err := service.Delete(ctx, 1)
|
||||
assert.Error(t, err)
|
||||
|
||||
appErr, ok := err.(*errors.AppError)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, errors.CodeUnauthorized, appErr.Code)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user