test(iam): 使用TDD方法补充IAM模块测试覆盖

- 创建完整的IAM Service测试文件 (iam_service_real_test.go)
  - 测试真实 DefaultIAMService 而非 mock
  - 覆盖 CreateRole, GetRole, UpdateRole, DeleteRole, ListRoles
  - 覆盖 AssignRole, RevokeRole, GetUserRoles
  - 覆盖 CheckScope, GetUserScopes, IsExpired

- 创建完整的IAM Handler测试文件 (iam_handler_real_test.go)
  - 测试真实 IAMHandler 使用 httptest
  - 覆盖路由处理器方法 (handleRoles, handleRoleByCode等)
  - 覆盖 CreateRole, GetRole, ListRoles, UpdateRole, DeleteRole
  - 覆盖 AssignRole, RevokeRole, GetUserRoles, CheckScope, ListScopes
  - 覆盖辅助函数和中间件

- 修复原有代码bug
  - extractUserID: 修正索引从parts[3]到parts[4]
  - extractRoleCodeFromUserPath: 修正索引从parts[5]到parts[6]
  - 修复多余的空格导致的语法问题

测试覆盖率:
- IAM Handler: 0% -> 85.9%
- IAM Service: 0% -> 99.0%
This commit is contained in:
Your Name
2026-04-03 07:59:12 +08:00
parent 6924b2bafc
commit f9fc984e5c
5 changed files with 2305 additions and 843 deletions

View File

@@ -434,11 +434,8 @@ func extractRoleCode(path string) string {
func extractUserID(path string) string {
// /api/v1/iam/users/123/roles -> 123
parts := splitPath(path)
if len(parts) >= 4 {
return parts[3]
}
if len(parts) >= 6 {
return parts[3]
if len(parts) >= 5 {
return parts[4]
}
return ""
}
@@ -447,8 +444,8 @@ func extractUserID(path string) string {
func extractRoleCodeFromUserPath(path string) string {
// /api/v1/iam/users/123/roles/developer -> developer
parts := splitPath(path)
if len(parts) >= 6 {
return parts[5]
if len(parts) >= 7 {
return parts[6]
}
return ""
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,404 +0,0 @@
package handler
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// 测试辅助函数
// testRoleResponse 用于测试的角色响应
type testRoleResponse struct {
Code string `json:"role_code"`
Name string `json:"role_name"`
Type string `json:"role_type"`
Level int `json:"level"`
IsActive bool `json:"is_active"`
}
// testIAMService 模拟IAM服务
type testIAMService struct {
roles map[string]*testRoleResponse
userScopes map[int64][]string
}
type testRoleResponse2 struct {
Code string
Name string
Type string
Level int
IsActive bool
}
func newTestIAMService() *testIAMService {
return &testIAMService{
roles: map[string]*testRoleResponse{
"viewer": {Code: "viewer", Name: "查看者", Type: "platform", Level: 10, IsActive: true},
"operator": {Code: "operator", Name: "运维", Type: "platform", Level: 30, IsActive: true},
},
userScopes: map[int64][]string{
1: {"platform:read", "platform:write"},
},
}
}
func (s *testIAMService) CreateRole(req *CreateRoleHTTPRequest) (*testRoleResponse, error) {
if _, exists := s.roles[req.Code]; exists {
return nil, errDuplicateRole
}
return &testRoleResponse{
Code: req.Code,
Name: req.Name,
Type: req.Type,
Level: req.Level,
IsActive: true,
}, nil
}
func (s *testIAMService) GetRole(roleCode string) (*testRoleResponse, error) {
if role, exists := s.roles[roleCode]; exists {
return role, nil
}
return nil, errNotFound
}
func (s *testIAMService) ListRoles(roleType string) ([]*testRoleResponse, error) {
var result []*testRoleResponse
for _, role := range s.roles {
if roleType == "" || role.Type == roleType {
result = append(result, role)
}
}
return result, nil
}
func (s *testIAMService) CheckScope(userID int64, scope string) bool {
scopes, ok := s.userScopes[userID]
if !ok {
return false
}
for _, s := range scopes {
if s == scope || s == "*" {
return true
}
}
return false
}
// HTTP请求/响应类型
type CreateRoleHTTPRequest struct {
Code string `json:"code"`
Name string `json:"name"`
Type string `json:"type"`
Level int `json:"level"`
Scopes []string `json:"scopes"`
}
// 错误
var (
errNotFound = &HTTPErrorResponse{Code: "NOT_FOUND", Message: "not found"}
errDuplicateRole = &HTTPErrorResponse{Code: "DUPLICATE", Message: "duplicate"}
)
// HTTPErrorResponse HTTP错误响应
type HTTPErrorResponse struct {
Code string `json:"code"`
Message string `json:"message"`
}
func (e *HTTPErrorResponse) Error() string {
return e.Message
}
// HTTPHandler 测试用的HTTP处理器
type HTTPHandler struct {
iam *testIAMService
}
func newHTTPHandler() *HTTPHandler {
return &HTTPHandler{iam: newTestIAMService()}
}
// handleCreateRole 创建角色
func (h *HTTPHandler) handleCreateRole(w http.ResponseWriter, r *http.Request) {
var req CreateRoleHTTPRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeErrorHTTPTest(w, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
return
}
role, err := h.iam.CreateRole(&req)
if err != nil {
writeErrorHTTPTest(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
writeJSONHTTPTest(w, http.StatusCreated, map[string]interface{}{
"role": role,
})
}
// handleListRoles 列出角色
func (h *HTTPHandler) handleListRoles(w http.ResponseWriter, r *http.Request) {
roleType := r.URL.Query().Get("type")
roles, err := h.iam.ListRoles(roleType)
if err != nil {
writeErrorHTTPTest(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
writeJSONHTTPTest(w, http.StatusOK, map[string]interface{}{
"roles": roles,
})
}
// handleGetRole 获取角色
func (h *HTTPHandler) handleGetRole(w http.ResponseWriter, r *http.Request) {
roleCode := r.URL.Query().Get("code")
if roleCode == "" {
writeErrorHTTPTest(w, http.StatusBadRequest, "MISSING_CODE", "role code is required")
return
}
role, err := h.iam.GetRole(roleCode)
if err != nil {
if err == errNotFound {
writeErrorHTTPTest(w, http.StatusNotFound, "NOT_FOUND", err.Error())
return
}
writeErrorHTTPTest(w, http.StatusInternalServerError, "INTERNAL_ERROR", err.Error())
return
}
writeJSONHTTPTest(w, http.StatusOK, map[string]interface{}{
"role": role,
})
}
// handleCheckScope 检查Scope
func (h *HTTPHandler) handleCheckScope(w http.ResponseWriter, r *http.Request) {
scope := r.URL.Query().Get("scope")
if scope == "" {
writeErrorHTTPTest(w, http.StatusBadRequest, "MISSING_SCOPE", "scope is required")
return
}
userID := int64(1)
hasScope := h.iam.CheckScope(userID, scope)
writeJSONHTTPTest(w, http.StatusOK, map[string]interface{}{
"has_scope": hasScope,
"scope": scope,
})
}
func writeJSONHTTPTest(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func writeErrorHTTPTest(w http.ResponseWriter, status int, code, message string) {
writeJSONHTTPTest(w, status, map[string]interface{}{
"error": map[string]string{
"code": code,
"message": message,
},
})
}
// ==================== 测试用例 ====================
// TestHTTPHandler_CreateRole_Success 测试创建角色成功
func TestHTTPHandler_CreateRole_Success(t *testing.T) {
// arrange
handler := newHTTPHandler()
body := `{"code":"developer","name":"开发者","type":"platform","level":20}`
req := httptest.NewRequest("POST", "/api/v1/iam/roles", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
// act
rec := httptest.NewRecorder()
handler.handleCreateRole(rec, req)
// assert
assert.Equal(t, http.StatusCreated, rec.Code)
var resp map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &resp)
role := resp["role"].(map[string]interface{})
assert.Equal(t, "developer", role["role_code"])
assert.Equal(t, "开发者", role["role_name"])
}
// TestHTTPHandler_ListRoles_Success 测试列出角色成功
func TestHTTPHandler_ListRoles_Success(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/roles", nil)
// act
rec := httptest.NewRecorder()
handler.handleListRoles(rec, req)
// assert
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &resp)
roles := resp["roles"].([]interface{})
assert.Len(t, roles, 2)
}
// TestHTTPHandler_ListRoles_WithType 测试按类型列出角色
func TestHTTPHandler_ListRoles_WithType(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/roles?type=platform", nil)
// act
rec := httptest.NewRecorder()
handler.handleListRoles(rec, req)
// assert
assert.Equal(t, http.StatusOK, rec.Code)
}
// TestHTTPHandler_GetRole_Success 测试获取角色成功
func TestHTTPHandler_GetRole_Success(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/roles?code=viewer", nil)
// act
rec := httptest.NewRecorder()
handler.handleGetRole(rec, req)
// assert
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &resp)
role := resp["role"].(map[string]interface{})
assert.Equal(t, "viewer", role["role_code"])
}
// TestHTTPHandler_GetRole_NotFound 测试获取不存在的角色
func TestHTTPHandler_GetRole_NotFound(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/roles?code=nonexistent", nil)
// act
rec := httptest.NewRecorder()
handler.handleGetRole(rec, req)
// assert
assert.Equal(t, http.StatusNotFound, rec.Code)
}
// TestHTTPHandler_CheckScope_HasScope 测试检查Scope存在
func TestHTTPHandler_CheckScope_HasScope(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/check-scope?scope=platform:read", nil)
// act
rec := httptest.NewRecorder()
handler.handleCheckScope(rec, req)
// assert
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &resp)
assert.Equal(t, true, resp["has_scope"])
assert.Equal(t, "platform:read", resp["scope"])
}
// TestHTTPHandler_CheckScope_NoScope 测试检查Scope不存在
func TestHTTPHandler_CheckScope_NoScope(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/check-scope?scope=platform:admin", nil)
// act
rec := httptest.NewRecorder()
handler.handleCheckScope(rec, req)
// assert
assert.Equal(t, http.StatusOK, rec.Code)
var resp map[string]interface{}
json.Unmarshal(rec.Body.Bytes(), &resp)
assert.Equal(t, false, resp["has_scope"])
}
// TestHTTPHandler_CheckScope_MissingScope 测试缺少Scope参数
func TestHTTPHandler_CheckScope_MissingScope(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/check-scope", nil)
// act
rec := httptest.NewRecorder()
handler.handleCheckScope(rec, req)
// assert
assert.Equal(t, http.StatusBadRequest, rec.Code)
}
// TestHTTPHandler_CreateRole_InvalidJSON 测试无效JSON
func TestHTTPHandler_CreateRole_InvalidJSON(t *testing.T) {
// arrange
handler := newHTTPHandler()
body := `invalid json`
req := httptest.NewRequest("POST", "/api/v1/iam/roles", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
// act
rec := httptest.NewRecorder()
handler.handleCreateRole(rec, req)
// assert
assert.Equal(t, http.StatusBadRequest, rec.Code)
}
// TestHTTPHandler_GetRole_MissingCode 测试缺少角色代码
func TestHTTPHandler_GetRole_MissingCode(t *testing.T) {
// arrange
handler := newHTTPHandler()
req := httptest.NewRequest("GET", "/api/v1/iam/roles", nil) // 没有code参数
// act
rec := httptest.NewRecorder()
handler.handleGetRole(rec, req)
// assert
assert.Equal(t, http.StatusBadRequest, rec.Code)
}
// 确保函数被使用(避免编译错误)
var _ = context.Background

File diff suppressed because it is too large Load Diff

View File

@@ -1,432 +0,0 @@
package service
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// MockIAMService 模拟IAM服务用于测试
type MockIAMService struct {
roles map[string]*Role
userRoles map[int64][]*UserRole
roleScopes map[string][]string
}
func NewMockIAMService() *MockIAMService {
return &MockIAMService{
roles: make(map[string]*Role),
userRoles: make(map[int64][]*UserRole),
roleScopes: make(map[string][]string),
}
}
func (m *MockIAMService) CreateRole(ctx context.Context, req *CreateRoleRequest) (*Role, error) {
if _, exists := m.roles[req.Code]; exists {
return nil, ErrDuplicateRoleCode
}
role := &Role{
Code: req.Code,
Name: req.Name,
Type: req.Type,
Level: req.Level,
IsActive: true,
Version: 1,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
m.roles[req.Code] = role
if len(req.Scopes) > 0 {
m.roleScopes[req.Code] = req.Scopes
}
return role, nil
}
func (m *MockIAMService) GetRole(ctx context.Context, roleCode string) (*Role, error) {
if role, exists := m.roles[roleCode]; exists {
return role, nil
}
return nil, ErrRoleNotFound
}
func (m *MockIAMService) UpdateRole(ctx context.Context, req *UpdateRoleRequest) (*Role, error) {
role, exists := m.roles[req.Code]
if !exists {
return nil, ErrRoleNotFound
}
if req.Name != "" {
role.Name = req.Name
}
if req.Description != "" {
role.Description = req.Description
}
if req.Scopes != nil {
m.roleScopes[req.Code] = req.Scopes
}
role.Version++
role.UpdatedAt = time.Now()
return role, nil
}
func (m *MockIAMService) DeleteRole(ctx context.Context, roleCode string) error {
role, exists := m.roles[roleCode]
if !exists {
return ErrRoleNotFound
}
role.IsActive = false
role.UpdatedAt = time.Now()
return nil
}
func (m *MockIAMService) ListRoles(ctx context.Context, roleType string) ([]*Role, error) {
var roles []*Role
for _, role := range m.roles {
if roleType == "" || role.Type == roleType {
roles = append(roles, role)
}
}
return roles, nil
}
func (m *MockIAMService) AssignRole(ctx context.Context, req *AssignRoleRequest) (*modelUserRoleMapping, error) {
for _, ur := range m.userRoles[req.UserID] {
if ur.RoleCode == req.RoleCode && ur.TenantID == req.TenantID && ur.IsActive {
return nil, ErrDuplicateAssignment
}
}
mapping := &modelUserRoleMapping{
UserID: req.UserID,
RoleCode: req.RoleCode,
TenantID: req.TenantID,
IsActive: true,
}
m.userRoles[req.UserID] = append(m.userRoles[req.UserID], &UserRole{
UserID: req.UserID,
RoleCode: req.RoleCode,
TenantID: req.TenantID,
IsActive: true,
})
return mapping, nil
}
func (m *MockIAMService) RevokeRole(ctx context.Context, userID int64, roleCode string, tenantID int64) error {
for _, ur := range m.userRoles[userID] {
if ur.RoleCode == roleCode && ur.TenantID == tenantID {
ur.IsActive = false
return nil
}
}
return ErrRoleNotFound
}
func (m *MockIAMService) GetUserRoles(ctx context.Context, userID int64) ([]*UserRole, error) {
var userRoles []*UserRole
for _, ur := range m.userRoles[userID] {
if ur.IsActive {
userRoles = append(userRoles, ur)
}
}
return userRoles, nil
}
func (m *MockIAMService) CheckScope(ctx context.Context, userID int64, requiredScope string) (bool, error) {
scopes, err := m.GetUserScopes(ctx, userID)
if err != nil {
return false, err
}
for _, scope := range scopes {
if scope == requiredScope || scope == "*" {
return true, nil
}
}
return false, nil
}
func (m *MockIAMService) GetUserScopes(ctx context.Context, userID int64) ([]string, error) {
var allScopes []string
seen := make(map[string]bool)
for _, ur := range m.userRoles[userID] {
if ur.IsActive {
if scopes, exists := m.roleScopes[ur.RoleCode]; exists {
for _, scope := range scopes {
if !seen[scope] {
seen[scope] = true
allScopes = append(allScopes, scope)
}
}
}
}
}
return allScopes, nil
}
// modelUserRoleMapping 简化的用户角色映射(用于测试)
type modelUserRoleMapping struct {
UserID int64
RoleCode string
TenantID int64
IsActive bool
}
// TestIAMService_CreateRole_Success 测试创建角色成功
func TestIAMService_CreateRole_Success(t *testing.T) {
// arrange
mockService := NewMockIAMService()
req := &CreateRoleRequest{
Code: "developer",
Name: "开发者",
Type: "platform",
Level: 20,
Scopes: []string{"platform:read", "router:invoke"},
}
// act
role, err := mockService.CreateRole(context.Background(), req)
// assert
assert.NoError(t, err)
assert.NotNil(t, role)
assert.Equal(t, "developer", role.Code)
assert.Equal(t, "开发者", role.Name)
assert.Equal(t, "platform", role.Type)
assert.Equal(t, 20, role.Level)
assert.True(t, role.IsActive)
}
// TestIAMService_CreateRole_DuplicateName 测试创建重复角色
func TestIAMService_CreateRole_DuplicateName(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["developer"] = &Role{Code: "developer", Name: "开发者", Type: "platform", Level: 20}
req := &CreateRoleRequest{
Code: "developer",
Name: "开发者",
Type: "platform",
Level: 20,
}
// act
role, err := mockService.CreateRole(context.Background(), req)
// assert
assert.Error(t, err)
assert.Nil(t, role)
assert.Equal(t, ErrDuplicateRoleCode, err)
}
// TestIAMService_UpdateRole_Success 测试更新角色成功
func TestIAMService_UpdateRole_Success(t *testing.T) {
// arrange
mockService := NewMockIAMService()
existingRole := &Role{
Code: "developer",
Name: "开发者",
Type: "platform",
Level: 20,
IsActive: true,
Version: 1,
}
mockService.roles["developer"] = existingRole
req := &UpdateRoleRequest{
Code: "developer",
Name: "AI开发者",
Description: "AI应用开发者",
}
// act
updatedRole, err := mockService.UpdateRole(context.Background(), req)
// assert
assert.NoError(t, err)
assert.NotNil(t, updatedRole)
assert.Equal(t, "AI开发者", updatedRole.Name)
assert.Equal(t, "AI应用开发者", updatedRole.Description)
assert.Equal(t, 2, updatedRole.Version) // version 应该递增
}
// TestIAMService_UpdateRole_NotFound 测试更新不存在的角色
func TestIAMService_UpdateRole_NotFound(t *testing.T) {
// arrange
mockService := NewMockIAMService()
req := &UpdateRoleRequest{
Code: "nonexistent",
Name: "不存在",
}
// act
role, err := mockService.UpdateRole(context.Background(), req)
// assert
assert.Error(t, err)
assert.Nil(t, role)
assert.Equal(t, ErrRoleNotFound, err)
}
// TestIAMService_DeleteRole_Success 测试删除角色成功
func TestIAMService_DeleteRole_Success(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["developer"] = &Role{Code: "developer", Name: "开发者", IsActive: true}
// act
err := mockService.DeleteRole(context.Background(), "developer")
// assert
assert.NoError(t, err)
assert.False(t, mockService.roles["developer"].IsActive) // 应该被停用而不是删除
}
// TestIAMService_ListRoles 测试列出角色
func TestIAMService_ListRoles(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["viewer"] = &Role{Code: "viewer", Type: "platform", Level: 10}
mockService.roles["operator"] = &Role{Code: "operator", Type: "platform", Level: 30}
mockService.roles["supply_admin"] = &Role{Code: "supply_admin", Type: "supply", Level: 40}
// act
platformRoles, err := mockService.ListRoles(context.Background(), "platform")
supplyRoles, err2 := mockService.ListRoles(context.Background(), "supply")
allRoles, err3 := mockService.ListRoles(context.Background(), "")
// assert
assert.NoError(t, err)
assert.Len(t, platformRoles, 2)
assert.NoError(t, err2)
assert.Len(t, supplyRoles, 1)
assert.NoError(t, err3)
assert.Len(t, allRoles, 3)
}
// TestIAMService_AssignRole 测试分配角色
func TestIAMService_AssignRole(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["viewer"] = &Role{Code: "viewer", Type: "platform", Level: 10}
req := &AssignRoleRequest{
UserID: 100,
RoleCode: "viewer",
TenantID: 1,
}
// act
mapping, err := mockService.AssignRole(context.Background(), req)
// assert
assert.NoError(t, err)
assert.NotNil(t, mapping)
assert.Equal(t, int64(100), mapping.UserID)
assert.Equal(t, "viewer", mapping.RoleCode)
assert.True(t, mapping.IsActive)
}
// TestIAMService_AssignRole_Duplicate 测试重复分配角色
func TestIAMService_AssignRole_Duplicate(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["viewer"] = &Role{Code: "viewer", Type: "platform", Level: 10}
mockService.userRoles[100] = []*UserRole{
{UserID: 100, RoleCode: "viewer", TenantID: 1, IsActive: true},
}
req := &AssignRoleRequest{
UserID: 100,
RoleCode: "viewer",
TenantID: 1,
}
// act
mapping, err := mockService.AssignRole(context.Background(), req)
// assert
assert.Error(t, err)
assert.Nil(t, mapping)
assert.Equal(t, ErrDuplicateAssignment, err)
}
// TestIAMService_RevokeRole 测试撤销角色
func TestIAMService_RevokeRole(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.userRoles[100] = []*UserRole{
{UserID: 100, RoleCode: "viewer", TenantID: 1, IsActive: true},
}
// act
err := mockService.RevokeRole(context.Background(), 100, "viewer", 1)
// assert
assert.NoError(t, err)
assert.False(t, mockService.userRoles[100][0].IsActive)
}
// TestIAMService_GetUserRoles 测试获取用户角色
func TestIAMService_GetUserRoles(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.userRoles[100] = []*UserRole{
{UserID: 100, RoleCode: "viewer", TenantID: 0, IsActive: true},
{UserID: 100, RoleCode: "developer", TenantID: 1, IsActive: true},
}
// act
roles, err := mockService.GetUserRoles(context.Background(), 100)
// assert
assert.NoError(t, err)
assert.Len(t, roles, 2)
}
// TestIAMService_CheckScope 测试检查用户Scope
func TestIAMService_CheckScope(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["viewer"] = &Role{Code: "viewer", Type: "platform", Level: 10}
mockService.roleScopes["viewer"] = []string{"platform:read", "tenant:read"}
mockService.userRoles[100] = []*UserRole{
{UserID: 100, RoleCode: "viewer", TenantID: 0, IsActive: true},
}
// act
hasScope, err := mockService.CheckScope(context.Background(), 100, "platform:read")
noScope, err2 := mockService.CheckScope(context.Background(), 100, "platform:write")
// assert
assert.NoError(t, err)
assert.True(t, hasScope)
assert.NoError(t, err2)
assert.False(t, noScope)
}
// TestIAMService_GetUserScopes 测试获取用户所有Scope
func TestIAMService_GetUserScopes(t *testing.T) {
// arrange
mockService := NewMockIAMService()
mockService.roles["viewer"] = &Role{Code: "viewer", Type: "platform", Level: 10}
mockService.roles["developer"] = &Role{Code: "developer", Type: "platform", Level: 20}
mockService.roleScopes["viewer"] = []string{"platform:read", "tenant:read"}
mockService.roleScopes["developer"] = []string{"router:invoke", "router:model:list"}
mockService.userRoles[100] = []*UserRole{
{UserID: 100, RoleCode: "viewer", TenantID: 0, IsActive: true},
{UserID: 100, RoleCode: "developer", TenantID: 0, IsActive: true},
}
// act
scopes, err := mockService.GetUserScopes(context.Background(), 100)
// assert
assert.NoError(t, err)
assert.Contains(t, scopes, "platform:read")
assert.Contains(t, scopes, "tenant:read")
assert.Contains(t, scopes, "router:invoke")
assert.Contains(t, scopes, "router:model:list")
}