All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m17s
- 合并 customer_account 和 shop_account 路由到统一的 account 接口 - 新增统一认证接口 (auth handler) - 实现越权防护中间件和权限检查工具函数 - 新增操作审计日志模型和服务 - 更新数据库迁移 (版本 39: account_operation_log 表) - 补充集成测试覆盖权限检查和审计日志场景
496 lines
16 KiB
Go
496 lines
16 KiB
Go
package integration
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/response"
|
|
"github.com/break/junhong_cmp_fiber/tests/testutils/integ"
|
|
)
|
|
|
|
// TestAccountPermission_12_2 企业账号访问账号管理接口被路由层拦截
|
|
func TestAccountPermission_12_2(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("测试店铺", 1, nil)
|
|
enterprise := env.CreateTestEnterprise("测试企业", &shop.ID)
|
|
|
|
enterpriseAccount := env.CreateTestAccount(
|
|
"enterprise_user",
|
|
"password123",
|
|
constants.UserTypeEnterprise,
|
|
nil,
|
|
&enterprise.ID,
|
|
)
|
|
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("test_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeEnterprise,
|
|
EnterpriseID: &enterprise.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(enterpriseAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode, "企业账号应被路由层拦截")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, result.Message, "权限", "错误消息应包含权限相关信息")
|
|
}
|
|
|
|
// TestAccountPermission_12_3 代理账号创建自己店铺的账号成功
|
|
func TestAccountPermission_12_3(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("代理店铺1", 1, nil)
|
|
|
|
agentAccount := env.CreateTestAccount(
|
|
"agent_user",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop.ID,
|
|
nil,
|
|
)
|
|
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("agent_same_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &shop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(agentAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
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, "业务码应为0")
|
|
}
|
|
|
|
// TestAccountPermission_12_4 代理账号创建下级店铺的账号成功
|
|
func TestAccountPermission_12_4(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
parentShop := env.CreateTestShop("父店铺", 1, nil)
|
|
childShop := env.CreateTestShop("子店铺", 2, &parentShop.ID)
|
|
|
|
agentAccount := env.CreateTestAccount(
|
|
"agent_parent",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&parentShop.ID,
|
|
nil,
|
|
)
|
|
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("agent_child_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &childShop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(agentAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
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, "业务码应为0")
|
|
}
|
|
|
|
// TestAccountPermission_12_5 代理账号创建其他店铺的账号失败
|
|
func TestAccountPermission_12_5(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop1 := env.CreateTestShop("独立店铺1", 1, nil)
|
|
shop2 := env.CreateTestShop("独立店铺2", 1, nil)
|
|
|
|
agentAccount := env.CreateTestAccount(
|
|
"agent_shop1",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop1.ID,
|
|
nil,
|
|
)
|
|
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("agent_other_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &shop2.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(agentAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode, "代理账号不应能创建其他店铺的账号")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, result.Message, "权限", "错误消息应包含权限相关信息")
|
|
}
|
|
|
|
// TestAccountPermission_12_6 代理账号创建平台账号失败
|
|
func TestAccountPermission_12_6(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("代理店铺", 1, nil)
|
|
|
|
agentAccount := env.CreateTestAccount(
|
|
"agent_try_platform",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop.ID,
|
|
nil,
|
|
)
|
|
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("platform_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypePlatform,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(agentAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode, "代理账号不应能创建平台账号")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, result.Message, "权限", "错误消息应包含权限相关信息")
|
|
}
|
|
|
|
// TestAccountPermission_12_7 平台账号创建任意类型账号成功
|
|
func TestAccountPermission_12_7(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("平台测试店铺", 1, nil)
|
|
|
|
platformAccount := env.CreateTestAccount(
|
|
"platform_user",
|
|
"password123",
|
|
constants.UserTypePlatform,
|
|
nil,
|
|
nil,
|
|
)
|
|
|
|
t.Run("创建平台账号", func(t *testing.T) {
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("platform_new_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypePlatform,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(platformAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "平台账号应能创建平台账号")
|
|
})
|
|
|
|
t.Run("创建代理账号", func(t *testing.T) {
|
|
time.Sleep(time.Millisecond)
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("agent_new_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &shop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(platformAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "平台账号应能创建代理账号")
|
|
})
|
|
}
|
|
|
|
// TestAccountPermission_12_8 超级管理员创建任意类型账号成功
|
|
func TestAccountPermission_12_8(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("超管测试店铺", 1, nil)
|
|
enterprise := env.CreateTestEnterprise("超管测试企业", &shop.ID)
|
|
|
|
t.Run("创建平台账号", func(t *testing.T) {
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("superadmin_platform_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypePlatform,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "超级管理员应能创建平台账号")
|
|
})
|
|
|
|
t.Run("创建代理账号", func(t *testing.T) {
|
|
time.Sleep(time.Millisecond)
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("superadmin_agent_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &shop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "超级管理员应能创建代理账号")
|
|
})
|
|
|
|
t.Run("创建企业账号", func(t *testing.T) {
|
|
time.Sleep(time.Millisecond)
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("superadmin_ent_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeEnterprise,
|
|
EnterpriseID: &enterprise.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsSuperAdmin().Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "超级管理员应能创建企业账号")
|
|
})
|
|
}
|
|
|
|
// TestAccountPermission_12_9 查询不存在的账号返回统一错误
|
|
func TestAccountPermission_12_9(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("查询测试店铺", 1, nil)
|
|
agentAccount := env.CreateTestAccount(
|
|
"agent_query",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop.ID,
|
|
nil,
|
|
)
|
|
|
|
resp, err := env.AsUser(agentAccount).Request("GET", "/api/admin/accounts/99999", nil)
|
|
require.NoError(t, err)
|
|
|
|
// GORM 自动过滤后,查询不存在的账号返回 "账号不存在" (400)
|
|
// 或 "无权限操作该资源或资源不存在" (403)
|
|
assert.True(t,
|
|
resp.StatusCode == fiber.StatusBadRequest ||
|
|
resp.StatusCode == fiber.StatusForbidden ||
|
|
resp.StatusCode == fiber.StatusNotFound,
|
|
"查询不存在的账号应返回错误状态码")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, 0, result.Code, "业务码应不为0")
|
|
}
|
|
|
|
// TestAccountPermission_12_10 查询越权的账号返回相同错误消息
|
|
func TestAccountPermission_12_10(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop1 := env.CreateTestShop("越权测试店铺1", 1, nil)
|
|
shop2 := env.CreateTestShop("越权测试店铺2", 1, nil)
|
|
|
|
agentAccount1 := env.CreateTestAccount(
|
|
"agent_auth1",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop1.ID,
|
|
nil,
|
|
)
|
|
|
|
agentAccount2 := env.CreateTestAccount(
|
|
"agent_auth2",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop2.ID,
|
|
nil,
|
|
)
|
|
|
|
resp, err := env.AsUser(agentAccount1).Request(
|
|
"GET",
|
|
fmt.Sprintf("/api/admin/accounts/%d", agentAccount2.ID),
|
|
nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// GORM 自动过滤使越权查询返回与不存在相同的错误,防止信息泄露
|
|
assert.True(t,
|
|
resp.StatusCode == fiber.StatusBadRequest ||
|
|
resp.StatusCode == fiber.StatusForbidden ||
|
|
resp.StatusCode == fiber.StatusNotFound,
|
|
"查询越权账号应返回错误状态码")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, 0, result.Code, "业务码应不为0")
|
|
}
|
|
|
|
// TestAccountPermission_12_11 代理账号更新其他店铺的账号失败
|
|
func TestAccountPermission_12_11(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop1 := env.CreateTestShop("更新测试店铺1", 1, nil)
|
|
shop2 := env.CreateTestShop("更新测试店铺2", 1, nil)
|
|
|
|
agentAccount1 := env.CreateTestAccount(
|
|
"agent_update1",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop1.ID,
|
|
nil,
|
|
)
|
|
|
|
agentAccount2 := env.CreateTestAccount(
|
|
"agent_update2",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&shop2.ID,
|
|
nil,
|
|
)
|
|
|
|
newUsername := "updated_username"
|
|
reqBody := dto.UpdateAccountRequest{
|
|
Username: &newUsername,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(agentAccount1).Request(
|
|
"PUT",
|
|
fmt.Sprintf("/api/admin/accounts/%d", agentAccount2.ID),
|
|
jsonBody,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode, "代理账号不应能更新其他店铺的账号")
|
|
|
|
var result response.Response
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, result.Message, "权限", "错误消息应包含权限相关信息")
|
|
}
|
|
|
|
// TestEnterpriseAccountRouteBlocking 测试企业账号访问各类型账号管理接口的路由层拦截
|
|
func TestEnterpriseAccountRouteBlocking(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
shop := env.CreateTestShop("路由拦截测试店铺", 1, nil)
|
|
enterprise := env.CreateTestEnterprise("路由拦截测试企业", &shop.ID)
|
|
|
|
enterpriseAccount := env.CreateTestAccount(
|
|
"enterprise_route_test",
|
|
"password123",
|
|
constants.UserTypeEnterprise,
|
|
nil,
|
|
&enterprise.ID,
|
|
)
|
|
|
|
t.Run("企业账号访问企业账号列表接口被拦截", func(t *testing.T) {
|
|
resp, err := env.AsUser(enterpriseAccount).Request("GET", "/api/admin/accounts", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("企业账号访问企业账号详情接口被拦截", func(t *testing.T) {
|
|
resp, err := env.AsUser(enterpriseAccount).Request("GET", "/api/admin/accounts/1", nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode)
|
|
})
|
|
|
|
t.Run("企业账号访问创建企业账号接口被拦截", func(t *testing.T) {
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("test_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeEnterprise,
|
|
EnterpriseID: &enterprise.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(enterpriseAccount).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode)
|
|
})
|
|
}
|
|
|
|
// TestAgentAccountHierarchyPermission 测试代理账号的层级权限
|
|
func TestAgentAccountHierarchyPermission(t *testing.T) {
|
|
env := integ.NewIntegrationTestEnv(t)
|
|
|
|
level1Shop := env.CreateTestShop("一级店铺", 1, nil)
|
|
level2Shop := env.CreateTestShop("二级店铺", 2, &level1Shop.ID)
|
|
level3Shop := env.CreateTestShop("三级店铺", 3, &level2Shop.ID)
|
|
|
|
level2Agent := env.CreateTestAccount(
|
|
"level2_agent",
|
|
"password123",
|
|
constants.UserTypeAgent,
|
|
&level2Shop.ID,
|
|
nil,
|
|
)
|
|
|
|
t.Run("二级代理可以管理三级店铺账号", func(t *testing.T) {
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("level3_new_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &level3Shop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(level2Agent).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusOK, resp.StatusCode, "二级代理应能管理三级店铺账号")
|
|
})
|
|
|
|
t.Run("二级代理不能管理一级店铺账号", func(t *testing.T) {
|
|
reqBody := dto.CreateAccountRequest{
|
|
Username: fmt.Sprintf("level1_new_%d", time.Now().UnixNano()),
|
|
Phone: fmt.Sprintf("138%08d", time.Now().UnixNano()%100000000),
|
|
Password: "Password123",
|
|
UserType: constants.UserTypeAgent,
|
|
ShopID: &level1Shop.ID,
|
|
}
|
|
jsonBody, _ := json.Marshal(reqBody)
|
|
|
|
resp, err := env.AsUser(level2Agent).Request("POST", "/api/admin/accounts", jsonBody)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, fiber.StatusForbidden, resp.StatusCode, "二级代理不应能管理一级店铺账号")
|
|
})
|
|
}
|