Files
lijiaoqiao/supply-api/internal/iam/scope_test.go
Your Name 8ac23bf7d4 test: improve coverage and fix sanitizer bug
- Fix MaskMap to properly handle []string sensitive fields
- Add missing slice handling in sanitizer
- Add comprehensive tests for GetMetrics and CreateEventsBatch
- Improve audit/handler coverage from 49.8% to 68.8%
- Fix test expectations to match actual sanitizer behavior
- All tests pass
2026-04-08 07:44:58 +08:00

391 lines
9.2 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 iam
import (
"testing"
)
// TestP102_ParseScope 解析scope字符串
func TestP102_ParseScope(t *testing.T) {
testCases := []struct {
name string
scopeStr string
wantErr bool
domain string
resource string
action string
}{
{
name: "valid supply scope",
scopeStr: "supply:accounts:read",
wantErr: false,
domain: "supply",
resource: "accounts",
action: "read",
},
{
name: "valid billing scope",
scopeStr: "billing:settlements:write",
wantErr: false,
domain: "billing",
resource: "settlements",
action: "write",
},
{
name: "invalid format",
scopeStr: "supply:accounts",
wantErr: true,
},
{
name: "invalid domain",
scopeStr: "unknown:accounts:read",
wantErr: true,
},
{
name: "invalid action",
scopeStr: "supply:accounts:unknown",
wantErr: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
scope, err := ParseScope(tc.scopeStr)
if tc.wantErr {
if err == nil {
t.Error("expected error but got nil")
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if scope.Domain != tc.domain {
t.Errorf("domain: expected %s, got %s", tc.domain, scope.Domain)
}
if scope.Resource != tc.resource {
t.Errorf("resource: expected %s, got %s", tc.resource, scope.Resource)
}
if scope.Action != tc.action {
t.Errorf("action: expected %s, got %s", tc.action, scope.Action)
}
}
})
}
t.Log("P1-02: scope解析验证通过")
}
// TestP102_HasScope 检查权限
func TestP102_HasScope(t *testing.T) {
userScopes := []string{
"supply:accounts:read",
"supply:accounts:write",
"supply:packages:read",
}
testCases := []struct {
name string
checkScope *Scope
expected bool
}{
{
name: "has exact scope",
checkScope: &Scope{
Domain: "supply",
Resource: "accounts",
Action: "read",
},
expected: true,
},
{
name: "has exact scope write",
checkScope: &Scope{
Domain: "supply",
Resource: "accounts",
Action: "write",
},
expected: true,
},
{
name: "has manage scope",
checkScope: &Scope{
Domain: "supply",
Resource: "accounts",
Action: "manage",
},
expected: false, // 用户没有manage但有read/write
},
{
name: "missing scope",
checkScope: &Scope{
Domain: "supply",
Resource: "orders",
Action: "read",
},
expected: false,
},
{
name: "admin has all",
checkScope: &Scope{
Domain: "billing",
Resource: "ledgers",
Action: "read",
},
expected: false, // 没有admin scope
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := HasScope(userScopes, tc.checkScope)
if result != tc.expected {
t.Errorf("expected %v, got %v", tc.expected, result)
}
})
}
t.Log("P1-02: HasScope验证通过")
}
// TestP102_HasScopeWithManage 验证manage权限
func TestP102_HasScopeWithManage(t *testing.T) {
userScopes := []string{
"supply:accounts:manage", // manage包含所有account操作
"supply:packages:read",
}
// 检查manage是否包含read
readScope := &Scope{
Domain: "supply",
Resource: "accounts",
Action: "read",
}
if !HasScope(userScopes, readScope) {
t.Error("manage should include read")
}
// 检查manage是否包含write
writeScope := &Scope{
Domain: "supply",
Resource: "accounts",
Action: "write",
}
if !HasScope(userScopes, writeScope) {
t.Error("manage should include write")
}
// 检查manage是否包含delete
deleteScope := &Scope{
Domain: "supply",
Resource: "accounts",
Action: "delete",
}
if !HasScope(userScopes, deleteScope) {
t.Error("manage should include delete")
}
t.Log("P1-02: manage权限验证通过")
}
// TestP102_HasAnyScope 任一权限检查
func TestP102_HasAnyScope(t *testing.T) {
userScopes := []string{
"supply:accounts:read",
}
requiredScopes := []*Scope{
{Domain: "supply", Resource: "accounts", Action: "read"},
{Domain: "supply", Resource: "packages", Action: "read"},
{Domain: "billing", Resource: "ledgers", Action: "read"},
}
if !HasAnyScope(userScopes, requiredScopes) {
t.Error("should return true when user has at least one scope")
}
t.Log("P1-02: HasAnyScope验证通过")
}
// TestP102_CommonScopes 常用scope定义
func TestP102_CommonScopes(t *testing.T) {
// 验证常用scope格式正确
scopes := []string{
CommonScopes.SupplyAccountsRead,
CommonScopes.SupplyAccountsWrite,
CommonScopes.SupplyAccountsDelete,
CommonScopes.SupplyAccountsManage,
CommonScopes.SupplyPackagesRead,
CommonScopes.BillingAccountsRead,
CommonScopes.BillingSettlementsWrite,
CommonScopes.IAMUsersRead,
CommonScopes.AuditEventsRead,
CommonScopes.AdminAll,
}
for _, scopeStr := range scopes {
scope, err := ParseScope(scopeStr)
if err != nil {
t.Errorf("invalid common scope %s: %v", scopeStr, err)
}
if scope == nil {
t.Errorf("failed to parse common scope %s", scopeStr)
}
}
t.Log("P1-02: 常用scope定义验证通过")
}
// TestP102_RoleScopes 角色默认scope
func TestP102_RoleScopes(t *testing.T) {
roles := []string{"viewer", "operator", "admin", "owner"}
for _, role := range roles {
scopes := GetScopesForRole(role)
if len(scopes) == 0 {
t.Errorf("role %s should have scopes", role)
}
// 验证每个scope都能正确解析
for _, scopeStr := range scopes {
_, err := ParseScope(scopeStr)
if err != nil {
t.Errorf("role %s has invalid scope %s: %v", role, scopeStr, err)
}
}
}
t.Log("P1-02: 角色默认scope验证通过")
}
// TestP102_Summary 测试总结
func TestP102_Summary(t *testing.T) {
t.Log("=== P1-02 Token Scope授权模型测试总结 ===")
t.Log("问题: Token runtime定义scope为string[]但未定义scope命名空间和授权规则")
t.Log("")
t.Log("修复方案:")
t.Log(" - scope格式: {domain}:{resource}:{action}")
t.Log(" - 域: supply, billing, iam, audit, admin")
t.Log(" - 动作: read, write, delete, manage, execute")
t.Log(" - manage权限包含所有其他权限")
t.Log(" - admin:admin:manage 包含所有权限")
t.Log("")
t.Log("示例scope:")
t.Log(" supply:accounts:read")
t.Log(" billing:settlements:write")
t.Log(" iam:users:manage")
}
// TestHasAllScopes_True tests HasAllScopes when user has all required scopes
func TestHasAllScopes_True(t *testing.T) {
userScopes := []string{
"supply:accounts:read",
"supply:accounts:write",
"supply:accounts:delete",
}
requiredScopes := []*Scope{
{Domain: "supply", Resource: "accounts", Action: "read"},
{Domain: "supply", Resource: "accounts", Action: "write"},
}
result := HasAllScopes(userScopes, requiredScopes)
if !result {
t.Error("expected true, user has all required scopes")
}
}
// TestHasAllScopes_False tests HasAllScopes when user is missing some scopes
func TestHasAllScopes_False(t *testing.T) {
userScopes := []string{
"supply:accounts:read",
}
requiredScopes := []*Scope{
{Domain: "supply", Resource: "accounts", Action: "read"},
{Domain: "supply", Resource: "accounts", Action: "write"},
}
result := HasAllScopes(userScopes, requiredScopes)
if result {
t.Error("expected false, user is missing write scope")
}
}
// TestHasAllScopes_EmptyRequired tests HasAllScopes with empty required scopes
func TestHasAllScopes_EmptyRequired(t *testing.T) {
userScopes := []string{"supply:accounts:read"}
result := HasAllScopes(userScopes, []*Scope{})
if !result {
t.Error("expected true, empty required scopes should return true")
}
}
// TestHasAllScopes_EmptyUser tests HasAllScopes with empty user scopes
func TestHasAllScopes_EmptyUser(t *testing.T) {
requiredScopes := []*Scope{
{Domain: "supply", Resource: "accounts", Action: "read"},
}
result := HasAllScopes([]string{}, requiredScopes)
if result {
t.Error("expected false, user has no scopes")
}
}
// TestInvalidScopeError tests the InvalidScopeError type
func TestInvalidScopeError(t *testing.T) {
err := &InvalidScopeError{
Scope: "invalid:scope:format",
Reason: "invalid format",
}
result := err.Error()
expected := "invalid scope 'invalid:scope:format': invalid format"
if result != expected {
t.Errorf("expected '%s', got '%s'", expected, result)
}
}
// TestHasAnyScope_False tests HasAnyScope when user has no matching scopes
func TestHasAnyScope_False(t *testing.T) {
userScopes := []string{
"supply:accounts:read",
}
requiredScopes := []*Scope{
{Domain: "billing", Resource: "ledgers", Action: "read"},
{Domain: "iam", Resource: "users", Action: "write"},
}
result := HasAnyScope(userScopes, requiredScopes)
if result {
t.Error("expected false, user has no matching scopes")
}
}
// TestHasAnyScope_EmptyRequired tests HasAnyScope with empty required scopes
func TestHasAnyScope_EmptyRequired(t *testing.T) {
userScopes := []string{"supply:accounts:read"}
result := HasAnyScope(userScopes, []*Scope{})
if result {
t.Error("expected false, empty required scopes should return false")
}
}
// TestHasAnyScope_EmptyUser tests HasAnyScope with empty user scopes
func TestHasAnyScope_EmptyUser(t *testing.T) {
requiredScopes := []*Scope{
{Domain: "supply", Resource: "accounts", Action: "read"},
}
result := HasAnyScope([]string{}, requiredScopes)
if result {
t.Error("expected false, user has no scopes")
}
}