Files
junhong_cmp_fiber/tests/integration/permission_test.go
huang 91c9bbfeb8
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m35s
feat: 实现账号与佣金管理模块
新增功能:
- 店铺佣金查询:店铺佣金统计、店铺佣金记录列表、店铺提现记录
- 佣金提现审批:提现申请列表、审批通过、审批拒绝
- 提现配置管理:配置列表、新增配置、获取当前生效配置
- 企业管理:企业列表、创建、更新、删除、获取详情
- 企业卡授权:授权列表、批量授权、批量取消授权、统计
- 客户账号管理:账号列表、创建、更新状态、重置密码
- 我的佣金:佣金统计、佣金记录、提现申请、提现记录

数据库变更:
- 扩展 tb_commission_withdrawal_request 新增提现单号等字段
- 扩展 tb_account 新增 is_primary 字段
- 扩展 tb_commission_record 新增 shop_id、balance_after
- 扩展 tb_commission_withdrawal_setting 新增每日提现次数限制
- 扩展 tb_iot_card、tb_device 新增 shop_id 冗余字段
- 新建 tb_enterprise_card_authorization 企业卡授权表
- 新建 tb_asset_allocation_record 资产分配记录表
- 数据迁移:owner_type 枚举值 agent 统一为 shop

测试:
- 新增 7 个单元测试文件覆盖各服务
- 修复集成测试 Redis 依赖问题
2026-01-21 18:20:44 +08:00

605 lines
18 KiB
Go

package integration
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http/httptest"
"testing"
"time"
"github.com/gofiber/fiber/v2"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
testcontainers_postgres "github.com/testcontainers/testcontainers-go/modules/postgres"
testcontainers_redis "github.com/testcontainers/testcontainers-go/modules/redis"
"github.com/testcontainers/testcontainers-go/wait"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/routes"
permissionService "github.com/break/junhong_cmp_fiber/internal/service/permission"
postgresStore "github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/break/junhong_cmp_fiber/pkg/response"
)
// permTestEnv 权限测试环境
type permTestEnv struct {
db *gorm.DB
redisClient *redis.Client
app *fiber.App
permissionService *permissionService.Service
cleanup func()
}
// setupPermTestEnv 设置权限测试环境
func setupPermTestEnv(t *testing.T) *permTestEnv {
t.Helper()
ctx := context.Background()
// 启动 PostgreSQL 容器
pgContainer, err := testcontainers_postgres.RunContainer(ctx,
testcontainers.WithImage("postgres:14-alpine"),
testcontainers_postgres.WithDatabase("testdb"),
testcontainers_postgres.WithUsername("postgres"),
testcontainers_postgres.WithPassword("password"),
testcontainers.WithWaitStrategy(
wait.ForLog("database system is ready to accept connections").
WithOccurrence(2).
WithStartupTimeout(30*time.Second),
),
)
require.NoError(t, err, "启动 PostgreSQL 容器失败")
pgConnStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable")
require.NoError(t, err)
// 启动 Redis 容器
redisContainer, err := testcontainers_redis.Run(ctx,
"redis:6-alpine",
)
require.NoError(t, err, "启动 Redis 容器失败")
redisHost, err := redisContainer.Host(ctx)
require.NoError(t, err)
redisPort, err := redisContainer.MappedPort(ctx, "6379")
require.NoError(t, err)
// 连接数据库
db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
require.NoError(t, err)
// 自动迁移
err = db.AutoMigrate(
&model.Permission{},
)
require.NoError(t, err)
// 连接 Redis
redisClient := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%s", redisHost, redisPort.Port()),
})
// 初始化 Store
permStore := postgresStore.NewPermissionStore(db)
accountRoleStore := postgresStore.NewAccountRoleStore(db, redisClient)
rolePermStore := postgresStore.NewRolePermissionStore(db, redisClient)
// 初始化 Service
permSvc := permissionService.New(permStore, accountRoleStore, rolePermStore, redisClient)
// 初始化 Handler
permHandler := admin.NewPermissionHandler(permSvc)
// 创建 Fiber App
app := fiber.New(fiber.Config{
ErrorHandler: func(c *fiber.Ctx, err error) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
},
})
// 注册路由
services := &bootstrap.Handlers{
Permission: permHandler,
}
middlewares := &bootstrap.Middlewares{}
routes.RegisterRoutes(app, services, middlewares)
return &permTestEnv{
db: db,
redisClient: redisClient,
app: app,
permissionService: permSvc,
cleanup: func() {
if err := pgContainer.Terminate(ctx); err != nil {
t.Logf("终止 PostgreSQL 容器失败: %v", err)
}
if err := redisContainer.Terminate(ctx); err != nil {
t.Logf("终止 Redis 容器失败: %v", err)
}
},
}
}
// TestPermissionAPI_Create 测试创建权限 API
func TestPermissionAPI_Create(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
t.Run("成功创建权限", func(t *testing.T) {
reqBody := model.CreatePermissionRequest{
PermName: "用户管理",
PermCode: "user:manage",
PermType: constants.PermissionTypeMenu,
URL: "/admin/users",
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/admin/permissions", bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
// 验证数据库中权限已创建
var count int64
env.db.Model(&model.Permission{}).Where("perm_code = ?", "user:manage").Count(&count)
assert.Equal(t, int64(1), count)
})
t.Run("权限编码重复时返回错误", func(t *testing.T) {
// 先创建一个权限
existingPerm := &model.Permission{
PermName: "已存在权限",
PermCode: "existing:perm",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(existingPerm)
// 尝试创建相同编码的权限
reqBody := model.CreatePermissionRequest{
PermName: "新权限",
PermCode: "existing:perm",
PermType: constants.PermissionTypeMenu,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/admin/permissions", bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, errors.CodePermCodeExists, result.Code)
})
t.Run("创建子权限", func(t *testing.T) {
// 先创建父权限
parentPerm := &model.Permission{
PermName: "系统管理",
PermCode: "system:manage",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(parentPerm)
// 创建子权限
reqBody := model.CreatePermissionRequest{
PermName: "用户列表",
PermCode: "system:user:list",
PermType: constants.PermissionTypeButton,
ParentID: &parentPerm.ID,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("POST", "/api/admin/permissions", bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
// 验证父权限ID已设置
var child model.Permission
env.db.Where("perm_code = ?", "system:user:list").First(&child)
assert.NotNil(t, child.ParentID)
assert.Equal(t, parentPerm.ID, *child.ParentID)
})
}
// TestPermissionAPI_Get 测试获取权限详情 API
func TestPermissionAPI_Get(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
// 创建测试权限
testPerm := &model.Permission{
PermName: "获取测试权限",
PermCode: "get:test:perm",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(testPerm)
t.Run("成功获取权限详情", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/%d", testPerm.ID), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("权限不存在时返回错误", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/99999", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, errors.CodePermissionNotFound, result.Code)
})
}
// TestPermissionAPI_Update 测试更新权限 API
func TestPermissionAPI_Update(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
// 创建测试权限
testPerm := &model.Permission{
PermName: "更新测试权限",
PermCode: "update:test:perm",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(testPerm)
t.Run("成功更新权限", func(t *testing.T) {
newName := "更新后权限"
reqBody := model.UpdatePermissionRequest{
PermName: &newName,
}
jsonBody, _ := json.Marshal(reqBody)
req := httptest.NewRequest("PUT", fmt.Sprintf("/api/admin/permissions/%d", testPerm.ID), bytes.NewReader(jsonBody))
req.Header.Set("Content-Type", "application/json")
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
// 验证数据库已更新
var updated model.Permission
env.db.First(&updated, testPerm.ID)
assert.Equal(t, newName, updated.PermName)
})
}
// TestPermissionAPI_Delete 测试删除权限 API
func TestPermissionAPI_Delete(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
t.Run("成功软删除权限", func(t *testing.T) {
// 创建测试权限
testPerm := &model.Permission{
PermName: "删除测试权限",
PermCode: "delete:test:perm",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(testPerm)
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/admin/permissions/%d", testPerm.ID), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
// 验证权限已软删除
var deleted model.Permission
err = env.db.Unscoped().First(&deleted, testPerm.ID).Error
require.NoError(t, err)
assert.NotNil(t, deleted.DeletedAt)
})
}
// TestPermissionAPI_List 测试权限列表 API
func TestPermissionAPI_List(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
// 创建多个测试权限
for i := 1; i <= 5; i++ {
perm := &model.Permission{
PermName: fmt.Sprintf("列表测试权限_%d", i),
PermCode: fmt.Sprintf("list:test:perm:%d", i),
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(perm)
}
t.Run("成功获取权限列表", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions?page=1&page_size=10", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按类型过滤权限", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions?perm_type=%d", constants.PermissionTypeMenu), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
}
// TestPermissionAPI_GetTree 测试获取权限树 API
func TestPermissionAPI_GetTree(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
// 添加测试中间件
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
// 创建层级权限结构
// 根权限
rootPerm := &model.Permission{
PermName: "系统管理",
PermCode: "system",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.db.Create(rootPerm)
// 子权限
childPerm := &model.Permission{
PermName: "用户管理",
PermCode: "system:user",
PermType: constants.PermissionTypeMenu,
ParentID: &rootPerm.ID,
Status: constants.StatusEnabled,
}
env.db.Create(childPerm)
// 孙子权限
grandchildPerm := &model.Permission{
PermName: "用户列表",
PermCode: "system:user:list",
PermType: constants.PermissionTypeButton,
ParentID: &childPerm.ID,
Status: constants.StatusEnabled,
}
env.db.Create(grandchildPerm)
t.Run("成功获取权限树", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/tree", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
}
// TestPermissionAPI_GetTreeByAvailableForRoleType 测试按角色类型过滤权限树 API
func TestPermissionAPI_GetTreeByRoleType(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
platformPerm := &model.Permission{
PermName: "平台权限",
PermCode: "platform:manage",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1",
Status: constants.StatusEnabled,
}
env.db.Create(platformPerm)
customerPerm := &model.Permission{
PermName: "客户权限",
PermCode: "customer:manage",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "2",
Status: constants.StatusEnabled,
}
env.db.Create(customerPerm)
commonPerm := &model.Permission{
PermName: "通用权限",
PermCode: "common:view",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1,2",
Status: constants.StatusEnabled,
}
env.db.Create(commonPerm)
t.Run("按角色类型过滤权限树-平台角色", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/tree?available_for_role_type=%d", constants.RoleTypePlatform), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按角色类型过滤权限树-客户角色", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/tree?available_for_role_type=2", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按平台和角色类型过滤", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions/tree?platform=all&available_for_role_type=1", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
}
// TestPermissionAPI_FilterByAvailableForRoleType 测试按角色类型过滤权限
func TestPermissionAPI_FilterByAvailableForRoleTypes(t *testing.T) {
env := setupPermTestEnv(t)
defer env.cleanup()
testUserID := uint(1)
env.app.Use(func(c *fiber.Ctx) error {
ctx := middleware.SetUserContext(c.UserContext(), middleware.NewSimpleUserContext(testUserID, constants.UserTypeSuperAdmin, 0))
c.SetUserContext(ctx)
return c.Next()
})
platformPerm := &model.Permission{
PermName: "平台专用权限",
PermCode: "platform:only",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1",
Status: constants.StatusEnabled,
}
env.db.Create(platformPerm)
customerPerm := &model.Permission{
PermName: "客户专用权限",
PermCode: "customer:only",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "2",
Status: constants.StatusEnabled,
}
env.db.Create(customerPerm)
commonPerm := &model.Permission{
PermName: "通用权限",
PermCode: "common:all",
PermType: constants.PermissionTypeMenu,
AvailableForRoleTypes: "1,2",
Status: constants.StatusEnabled,
}
env.db.Create(commonPerm)
t.Run("过滤平台角色可用权限", func(t *testing.T) {
req := httptest.NewRequest("GET", "/api/admin/permissions?available_for_role_type=1", nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
t.Run("按角色类型过滤权限树", func(t *testing.T) {
req := httptest.NewRequest("GET", fmt.Sprintf("/api/admin/permissions/tree?available_for_role_type=%d", constants.RoleTypePlatform), nil)
resp, err := env.app.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
var result response.Response
err = json.NewDecoder(resp.Body).Decode(&result)
require.NoError(t, err)
assert.Equal(t, 0, result.Code)
})
}