Files
junhong_cmp_fiber/tests/integration/api_regression_test.go
huang 23eb0307bb
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m30s
feat: 实现门店套餐分配功能并统一测试基础设施
新增功能:
- 门店套餐分配管理(shop_package_allocation):支持门店套餐库存管理
- 门店套餐系列分配管理(shop_series_allocation):支持套餐系列分配和佣金层级设置
- 我的套餐查询(my_package):支持门店查询自己的套餐分配情况

测试改进:
- 统一集成测试基础设施,新增 testutils.NewIntegrationTestEnv
- 重构所有集成测试使用新的测试环境设置
- 移除旧的测试辅助函数和冗余测试文件
- 新增 test_helpers_test.go 统一任务测试辅助

技术细节:
- 新增数据库迁移 000025_create_shop_allocation_tables
- 新增 3 个 Handler、Service、Store 和对应的单元测试
- 更新 OpenAPI 文档和文档生成器
- 测试覆盖率:Service 层 > 90%

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 10:45:16 +08:00

236 lines
7.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package integration
import (
"fmt"
"net/http/httptest"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/tests/testutils/integ"
)
// TestAPIRegression_AllEndpointsAccessible 测试所有 API 端点在重构后仍可访问
func TestAPIRegression_AllEndpointsAccessible(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
// 定义所有需要测试的端点(检测端点是否存在,不检测业务逻辑)
endpoints := []struct {
method string
path string
name string
requiresAuth bool
}{
// Health endpoints无需认证
{"GET", "/health", "Health check", false},
// Account endpoints需要认证
{"GET", "/api/admin/accounts", "List accounts", true},
{"GET", "/api/admin/accounts/1", "Get account", true},
// Role endpoints需要认证
{"GET", "/api/admin/roles", "List roles", true},
{"GET", "/api/admin/roles/1", "Get role", true},
// Permission endpoints需要认证
{"GET", "/api/admin/permissions", "List permissions", true},
{"GET", "/api/admin/permissions/1", "Get permission", true},
{"GET", "/api/admin/permissions/tree", "Get permission tree", true},
}
for _, ep := range endpoints {
t.Run(ep.name, func(t *testing.T) {
var resp *httptest.ResponseRecorder
var err error
if ep.requiresAuth {
httpResp, httpErr := env.AsSuperAdmin().Request(ep.method, ep.path, nil)
require.NoError(t, httpErr)
resp = &httptest.ResponseRecorder{Code: httpResp.StatusCode}
err = nil
} else {
req := httptest.NewRequest(ep.method, ep.path, nil)
httpResp, httpErr := env.App.Test(req)
require.NoError(t, httpErr)
resp = &httptest.ResponseRecorder{Code: httpResp.StatusCode}
err = httpErr
}
_ = err
// 验证端点可访问(状态码不是 404 或 500
assert.NotEqual(t, fiber.StatusNotFound, resp.Code,
"端点 %s %s 应该存在", ep.method, ep.path)
assert.NotEqual(t, fiber.StatusInternalServerError, resp.Code,
"端点 %s %s 不应该返回 500 错误", ep.method, ep.path)
})
}
}
func TestAPIRegression_RouteModularization(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
t.Run("账号模块路由正常", func(t *testing.T) {
account := &model.Account{
Username: "regression_test",
Phone: "13800000300",
Password: "hashedpassword",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
env.TX.Create(account)
resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/accounts/%d", account.ID), nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
resp, err = env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/accounts/%d/roles", account.ID), nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
t.Run("角色模块路由正常", func(t *testing.T) {
role := &model.Role{
RoleName: "回归测试角色",
RoleType: constants.RoleTypePlatform,
Status: constants.StatusEnabled,
}
env.TX.Create(role)
resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/roles/%d", role.ID), nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
resp, err = env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/roles/%d/permissions", role.ID), nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
t.Run("权限模块路由正常", func(t *testing.T) {
perm := &model.Permission{
PermName: "回归测试权限",
PermCode: "regression:perm",
PermType: constants.PermissionTypeMenu,
Status: constants.StatusEnabled,
}
env.TX.Create(perm)
resp, err := env.AsSuperAdmin().Request("GET", fmt.Sprintf("/api/admin/permissions/%d", perm.ID), nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
resp, err = env.AsSuperAdmin().Request("GET", "/api/admin/permissions/tree", nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
}
// TestAPIRegression_ErrorHandling 测试错误处理在重构后仍正常
func TestAPIRegression_ErrorHandling(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
t.Run("资源不存在返回正确错误码", func(t *testing.T) {
// 账号不存在
req := httptest.NewRequest("GET", "/api/admin/accounts/99999", nil)
resp, err := env.App.Test(req)
require.NoError(t, err)
// 应该返回业务错误,不是 404
assert.NotEqual(t, fiber.StatusNotFound, resp.StatusCode)
// 角色不存在
req = httptest.NewRequest("GET", "/api/admin/roles/99999", nil)
resp, err = env.App.Test(req)
require.NoError(t, err)
assert.NotEqual(t, fiber.StatusNotFound, resp.StatusCode)
// 权限不存在
req = httptest.NewRequest("GET", "/api/admin/permissions/99999", nil)
resp, err = env.App.Test(req)
require.NoError(t, err)
assert.NotEqual(t, fiber.StatusNotFound, resp.StatusCode)
})
t.Run("无效参数返回正确错误码", func(t *testing.T) {
// 无效账号 ID
req := httptest.NewRequest("GET", "/api/admin/accounts/invalid", nil)
resp, err := env.App.Test(req)
require.NoError(t, err)
assert.NotEqual(t, fiber.StatusInternalServerError, resp.StatusCode)
})
}
func TestAPIRegression_Pagination(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
for i := 1; i <= 25; i++ {
account := &model.Account{
Username: fmt.Sprintf("pagination_test_%d", i),
Phone: fmt.Sprintf("138000004%02d", i),
Password: "hashedpassword",
UserType: constants.UserTypePlatform,
Status: constants.StatusEnabled,
}
env.TX.Create(account)
}
t.Run("分页参数正常工作", func(t *testing.T) {
resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/accounts?page=1&page_size=10", nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
resp, err = env.AsSuperAdmin().Request("GET", "/api/admin/accounts?page=2&page_size=10", nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
t.Run("默认分页参数工作", func(t *testing.T) {
resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/accounts", nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
}
func TestAPIRegression_ResponseFormat(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
t.Run("成功响应包含正确字段", func(t *testing.T) {
resp, err := env.AsSuperAdmin().Request("GET", "/api/admin/accounts", nil)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
assert.Contains(t, resp.Header.Get("Content-Type"), "application/json")
})
t.Run("健康检查端点响应正常", func(t *testing.T) {
req := httptest.NewRequest("GET", "/health", nil)
resp, err := env.App.Test(req)
require.NoError(t, err)
assert.Equal(t, fiber.StatusOK, resp.StatusCode)
})
}
// TestAPIRegression_ServicesIntegration 测试服务集成在重构后仍正常
func TestAPIRegression_ServicesIntegration(t *testing.T) {
env := integ.NewIntegrationTestEnv(t)
t.Run("Services 容器正确初始化", func(t *testing.T) {
// 验证所有模块路由都已注册
endpoints := []string{
"/health",
"/api/admin/accounts",
"/api/admin/roles",
"/api/admin/permissions",
}
for _, ep := range endpoints {
req := httptest.NewRequest("GET", ep, nil)
resp, err := env.App.Test(req)
require.NoError(t, err)
assert.NotEqual(t, fiber.StatusNotFound, resp.StatusCode,
"端点 %s 应该已注册", ep)
}
})
}