package role import ( "errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" permModel "perms-system-server/internal/model/perm" roleModel "perms-system-server/internal/model/role" "perms-system-server/internal/model/roleperm" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" "testing" "time" ) func TestRoleDetail_Normal(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() pc := testutil.UniqueId() roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{ ProductCode: pc, Name: testutil.UniqueId(), Remark: "detail test", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) roleId, _ := roleRes.LastInsertId() p1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{ ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) p1Id, _ := p1Res.LastInsertId() p2Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{ ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) p2Id, _ := p2Res.LastInsertId() rp1Res, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{ RoleId: roleId, PermId: p1Id, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) rp1Id, _ := rp1Res.LastInsertId() rp2Res, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{ RoleId: roleId, PermId: p2Id, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) rp2Id, _ := rp2Res.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role_perm`", rp1Id, rp2Id) testutil.CleanTable(ctx, conn, "`sys_perm`", p1Id, p2Id) testutil.CleanTable(ctx, conn, "`sys_role`", roleId) }) logic := NewRoleDetailLogic(ctx, svcCtx) resp, err := logic.RoleDetail(&types.RoleDetailReq{Id: roleId}) require.NoError(t, err) assert.Equal(t, roleId, resp.Id) assert.Equal(t, pc, resp.ProductCode) assert.Equal(t, "detail test", resp.Remark) assert.ElementsMatch(t, []int64{p1Id, p2Id}, resp.PermIds) } // TC-0125: 不存在 func TestRoleDetail_NotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewRoleDetailLogic(ctx, svcCtx) resp, err := logic.RoleDetail(&types.RoleDetailReq{Id: 999999999}) assert.Nil(t, resp) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 404, ce.Code()) assert.Equal(t, "角色不存在", ce.Error()) } func TestRoleDetail_MN3_CrossProductReturns404NotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() // 插入别的产品("test_product2")下的 role otherProduct := "mn3_other_" + testutil.UniqueId() roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{ ProductCode: otherProduct, Name: "mn3_role_" + testutil.UniqueId(), Remark: "mn3", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) roleId, _ := roleRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) }) // Admin 身份在 "test_product" 下访问 "mn3_other_*" 的 roleId adminCtx := ctxhelper.AdminCtx("test_product") resp, err := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: roleId}) assert.Nil(t, resp) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 404, ce.Code(), "跨产品访问必须 404,不得以 403 暴露存在性") assert.Equal(t, "角色不存在", ce.Error(), "响应文案必须与 'id 真实不存在' 完全一致,彻底关闭枚举 oracle") } // TC-1001: 对照 —— "id 不存在" 的响应必须与 "跨产品访问" 完全一致(code 与 body)。 func TestRoleDetail_MN3_NotFoundAndCrossProduct_Indistinguishable(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() ctx := ctxhelper.SuperAdminCtx() now := time.Now().Unix() // 预埋一条别的产品的角色 otherProduct := "mn3_cmp_" + testutil.UniqueId() roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{ ProductCode: otherProduct, Name: "mn3cmp_" + testutil.UniqueId(), Remark: "", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) existingId, _ := roleRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", existingId) }) adminCtx := ctxhelper.AdminCtx("test_product") // (A) 跨产品访问真实存在的 id _, errCross := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: existingId}) require.Error(t, errCross) var ceA *response.CodeError require.True(t, errors.As(errCross, &ceA)) // (B) 访问一个肯定不存在的 id(id 选在 existingId + 很大偏移,规避数据库递增到该值) _, errAbsent := NewRoleDetailLogic(adminCtx, svcCtx).RoleDetail( &types.RoleDetailReq{Id: existingId + 999_999_999}, ) require.Error(t, errAbsent) var ceB *response.CodeError require.True(t, errors.As(errAbsent, &ceB)) // 响应码与文案必须完全一致,不给任何侧信道 assert.Equal(t, ceB.Code(), ceA.Code(), "跨产品 vs id 不存在必须返回相同 code") assert.Equal(t, ceB.Error(), ceA.Error(), "跨产品 vs id 不存在必须返回相同 body") } // TC-1002: 超管跨产品仍然可以正常访问(/运维路径不能被误伤)。 func TestRoleDetail_MN3_SuperAdminCanStillAccessCrossProduct(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() now := time.Now().Unix() otherProduct := "mn3_sa_" + testutil.UniqueId() roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{ ProductCode: otherProduct, Name: "mn3sa_" + testutil.UniqueId(), Remark: "sa_cross", Status: 1, PermsLevel: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) roleId, _ := roleRes.LastInsertId() permRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{ ProductCode: otherProduct, Name: "mn3sa_p_" + testutil.UniqueId(), Code: testutil.UniqueId(), Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) permId, _ := permRes.LastInsertId() rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{ RoleId: roleId, PermId: permId, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) rpId, _ := rpRes.LastInsertId() t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId) testutil.CleanTable(ctx, conn, "`sys_perm`", permId) testutil.CleanTable(ctx, conn, "`sys_role`", roleId) }) // 超管在 "test_product" 当前身份,但跨产品访问 "mn3_sa_*" 的 role,应允许 resp, err := NewRoleDetailLogic(ctx, svcCtx).RoleDetail(&types.RoleDetailReq{Id: roleId}) require.NoError(t, err, "超管必须能跨产品查看 role,支撑/运维路径") require.NotNil(t, resp) assert.Equal(t, roleId, resp.Id) assert.Equal(t, otherProduct, resp.ProductCode) assert.Contains(t, resp.PermIds, permId) }