test: Add integration tests for org, role and oauth client

- Add OAuth client CRUD and access control tests
    - Add organization CRUD, tree and access control tests
    - Add role CRUD, access control and system role protection tests
    - Remove user:read permission from default user role
master
veypi 7 days ago
parent 63792b449f
commit 7f7591cf6d

@ -125,9 +125,8 @@ func init() {
VBaseAuth.AddRole(RoleCodeAdmin, "管理员", "*:*")
VBaseAuth.AddRole(RoleCodeUser, "普通用户",
"user:read",
"org:read",
"org:create",
"org:read",
"oauth-client:read",
"oauth-client:create",
"oauth-client:update",

@ -0,0 +1,120 @@
package tests
import (
"testing"
)
// OAuthClientResp OAuth 客户端响应
type OAuthClientResp struct {
ID string `json:"id"`
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret,omitempty"`
Name string `json:"name"`
RedirectURIs string `json:"redirect_uris"`
AllowedScopes string `json:"allowed_scopes"`
}
// Test OAuth Client CRUD
func TestOAuthClientCRUD(t *testing.T) {
ensureUsers(t)
var clientID string // This is ClientID (string), not ID (UUID)
// Test 1: List OAuth Clients
t.Run("List OAuth Clients", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/oauth/clients", nil, AdminToken)
assertStatus(t, resp, 200)
var data struct {
Items []OAuthClientResp `json:"items"`
}
decodeResponse(t, resp, &data)
t.Logf("Total OAuth clients: %d", len(data.Items))
})
// Test 2: Create OAuth Client
t.Run("Create OAuth Client", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/oauth/clients", map[string]interface{}{
"name": "Test OAuth Client",
"redirect_uris": []string{"https://example.com/callback"},
"allowed_scopes": "openid profile email",
}, AdminToken)
assertStatus(t, resp, 200)
var data OAuthClientResp
decodeResponse(t, resp, &data)
clientID = data.ClientID // Use ClientID, not ID
t.Logf("Created OAuth client: %s (ID: %s)", clientID, data.ID)
})
if clientID == "" {
t.Fatal("Failed to create OAuth client, skipping remaining tests")
}
// Test 3: Get OAuth Client Details
t.Run("Get OAuth Client Details", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
assertStatus(t, resp, 200)
var data OAuthClientResp
decodeResponse(t, resp, &data)
if data.Name != "Test OAuth Client" {
t.Errorf("Expected name 'Test OAuth Client', got '%s'", data.Name)
}
})
// Test 4: Update OAuth Client
t.Run("Update OAuth Client", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/oauth/clients/"+clientID, map[string]string{
"name": "Updated OAuth Client",
}, AdminToken)
assertStatus(t, resp, 200)
// Verify update
resp = doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
assertStatus(t, resp, 200)
var data OAuthClientResp
decodeResponse(t, resp, &data)
if data.Name != "Updated OAuth Client" {
t.Errorf("Expected name 'Updated OAuth Client', got '%s'", data.Name)
}
})
// Test 5: Delete OAuth Client
t.Run("Delete OAuth Client", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken)
assertStatus(t, resp, 200)
// Verify deletion
resp = doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
if resp.Code == 200 {
t.Errorf("Expected client to be deleted, but got 200")
} else {
t.Logf("Client deleted successfully, got code: %d", resp.Code)
}
})
}
// Test regular user OAuth client access
func TestOAuthClientAccessControl(t *testing.T) {
ensureUsers(t)
// Regular user should be able to list OAuth clients (oauth-client:read)
t.Run("Regular User List Clients", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/oauth/clients", nil, User1Token)
assertStatus(t, resp, 200)
})
// Regular user should be able to create OAuth clients (oauth-client:create)
t.Run("Regular User Create Client", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/oauth/clients", map[string]interface{}{
"name": "User OAuth Client",
"redirect_uris": []string{"https://example.com/callback"},
"allowed_scopes": "openid profile email",
}, User1Token)
assertStatus(t, resp, 200)
})
}

@ -0,0 +1,223 @@
package tests
import (
"testing"
)
func TestOrgCRUD(t *testing.T) {
ensureUsers(t)
var orgID string
// Test 1: Create Organization
t.Run("Create Organization", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "test_org_crud",
"name": "Test Org CRUD",
"description": "Organization for CRUD tests",
}, User1Token)
assertStatus(t, resp, 200)
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
orgID = data.ID
t.Logf("Created org: %s", orgID)
})
if orgID == "" {
t.Fatal("Failed to create org, skipping remaining tests")
}
// Test 2: List Organizations
t.Run("List Organizations", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs", nil, User1Token)
assertStatus(t, resp, 200)
var data struct {
Items []OrgResp `json:"items"`
Total int64 `json:"total"`
}
decodeResponse(t, resp, &data)
t.Logf("Total orgs: %d", data.Total)
if data.Total <= 0 {
t.Errorf("Expected at least 1 org, got %d", data.Total)
}
})
// Test 3: Get Org Details
t.Run("Get Org Details", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
var data OrgResp
decodeResponse(t, resp, &data)
if data.Name != "Test Org CRUD" {
t.Errorf("Expected name 'Test Org CRUD', got '%s'", data.Name)
}
if data.Code != "test_org_crud" {
t.Errorf("Expected code 'test_org_crud', got '%s'", data.Code)
}
})
// Test 4: Update Organization
t.Run("Update Organization", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/orgs/"+orgID, map[string]string{
"name": "Updated Org Name",
"description": "Updated description",
}, User1Token)
assertStatus(t, resp, 200)
// Verify update
resp = doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
var data OrgResp
decodeResponse(t, resp, &data)
if data.Name != "Updated Org Name" {
t.Errorf("Expected name 'Updated Org Name', got '%s'", data.Name)
}
})
// Test 5: Get Org Members
t.Run("Get Org Members", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID+"/members", nil, User1Token)
assertStatus(t, resp, 200)
var data struct {
Items []struct {
UserID string `json:"user_id"`
Role string `json:"role"`
} `json:"items"`
}
decodeResponse(t, resp, &data)
t.Logf("Org members count: %d", len(data.Items))
})
// Test 6: Delete Organization (as owner)
t.Run("Delete Organization", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
// Verify deletion
resp = doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
if resp.Code == 200 {
t.Errorf("Expected org to be deleted, but got 200")
} else {
t.Logf("Org deleted successfully, got code: %d", resp.Code)
}
})
// Test 7: List after deletion
t.Run("List After Delete", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs", nil, User1Token)
assertStatus(t, resp, 200)
var data struct {
Total int64 `json:"total"`
}
decodeResponse(t, resp, &data)
t.Logf("Total orgs after delete: %d", data.Total)
})
}
// Test Org Tree
func TestOrgTree(t *testing.T) {
ensureUsers(t)
// Create parent org
var parentOrgID string
t.Run("Create Parent Org", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "parent_org",
"name": "Parent Organization",
"description": "Parent org for tree test",
}, User1Token)
assertStatus(t, resp, 200)
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
parentOrgID = data.ID
})
if parentOrgID == "" {
t.Fatal("Failed to create parent org")
}
// Get org tree
t.Run("Get Org Tree", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/tree", nil, User1Token)
assertStatus(t, resp, 200)
// Tree endpoint returns array directly
var data []OrgResp
decodeResponse(t, resp, &data)
t.Logf("Org tree items: %d", len(data))
})
}
// Test non-member access
func TestOrgAccessControl(t *testing.T) {
ensureUsers(t)
// User1 creates org
var orgID string
t.Run("User1 Creates Org", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "private_org",
"name": "Private Organization",
"description": "Private org for access test",
}, User1Token)
assertStatus(t, resp, 200)
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
orgID = data.ID
})
if orgID == "" {
t.Fatal("Failed to create org")
}
// User2 (non-member) tries to access
t.Run("Non-member Get Org", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User2Token)
// Should fail with 403
if resp.Code == 200 {
t.Errorf("Expected non-member to be denied, got 200")
} else {
t.Logf("Non-member correctly denied access, code: %d", resp.Code)
}
})
t.Run("Non-member Update Org", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/orgs/"+orgID, map[string]string{
"name": "Hacked",
}, User2Token)
// Should fail with 403
if resp.Code == 200 {
t.Errorf("Expected non-member to be denied, got 200")
} else {
t.Logf("Non-member correctly denied update, code: %d", resp.Code)
}
})
t.Run("Non-member Get Members", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID+"/members", nil, User2Token)
// Should fail with 403
if resp.Code == 200 {
t.Errorf("Expected non-member to be denied, got 200")
} else {
t.Logf("Non-member correctly denied members access, code: %d", resp.Code)
}
})
}

@ -0,0 +1,204 @@
package tests
import (
"testing"
)
func TestRoleCRUD(t *testing.T) {
ensureUsers(t)
var roleID string
// Test 1: List Roles (as admin)
t.Run("List Roles", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/roles", nil, AdminToken)
assertStatus(t, resp, 200)
var data struct {
Items []struct {
ID string `json:"id"`
Code string `json:"code"`
Name string `json:"name"`
} `json:"items"`
}
decodeResponse(t, resp, &data)
t.Logf("Total roles: %d", len(data.Items))
if len(data.Items) == 0 {
t.Errorf("Expected some roles, got 0")
}
})
// Test 2: Create Role
t.Run("Create Role", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/roles", map[string]string{
"code": "test_role",
"name": "Test Role",
"description": "Role created for testing",
}, AdminToken)
assertStatus(t, resp, 200)
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
roleID = data.ID
t.Logf("Created role: %s", roleID)
})
if roleID == "" {
t.Fatal("Failed to create role, skipping remaining tests")
}
// Test 3: Get Role Details
t.Run("Get Role Details", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
assertStatus(t, resp, 200)
var data struct {
Code string `json:"code"`
Name string `json:"name"`
}
decodeResponse(t, resp, &data)
if data.Code != "test_role" {
t.Errorf("Expected code 'test_role', got '%s'", data.Code)
}
if data.Name != "Test Role" {
t.Errorf("Expected name 'Test Role', got '%s'", data.Name)
}
})
// Test 4: Update Role
t.Run("Update Role", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/roles/"+roleID, map[string]string{
"name": "Updated Test Role",
"description": "Updated description",
}, AdminToken)
assertStatus(t, resp, 200)
// Verify update
resp = doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
assertStatus(t, resp, 200)
var data struct {
Name string `json:"name"`
}
decodeResponse(t, resp, &data)
if data.Name != "Updated Test Role" {
t.Errorf("Expected name 'Updated Test Role', got '%s'", data.Name)
}
})
// Test 5: Get Role Permissions
t.Run("Get Role Permissions", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/roles/"+roleID+"/permissions", nil, AdminToken)
assertStatus(t, resp, 200)
// Returns array directly
var data []struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
t.Logf("Role permissions count: %d", len(data))
})
// Test 6: Update Role Permissions
t.Run("Update Role Permissions", func(t *testing.T) {
// First, get available permissions
resp := doRequest(t, "GET", "/api/roles", nil, AdminToken)
assertStatus(t, resp, 200)
// Try to update with an empty permission list first
resp = doRequest(t, "PUT", "/api/roles/"+roleID+"/permissions", map[string]interface{}{
"permission_ids": []string{},
}, AdminToken)
assertStatus(t, resp, 200)
t.Logf("Updated role permissions to empty")
})
// Test 7: Delete Role
t.Run("Delete Role", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/roles/"+roleID, nil, AdminToken)
assertStatus(t, resp, 200)
// Verify deletion
resp = doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
if resp.Code == 200 {
t.Errorf("Expected role to be deleted, but got 200")
} else {
t.Logf("Role deleted successfully, got code: %d", resp.Code)
}
})
}
// Test role access control
func TestRoleAccessControl(t *testing.T) {
ensureUsers(t)
// Regular user tries to access role endpoints - should fail (admin only)
t.Run("Regular User List Roles", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/roles", nil, User1Token)
// Should fail - role:read requires admin permission
if resp.Code == 200 {
t.Errorf("Expected regular user to be denied, got 200")
} else {
t.Logf("Regular user correctly denied list roles, code: %d", resp.Code)
}
})
t.Run("Regular User Create Role", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/roles", map[string]string{
"code": "illegal_role",
"name": "Should Fail",
"description": "Should not be created",
}, User1Token)
// Should fail - needs role:create permission
if resp.Code == 200 {
t.Errorf("Expected regular user to be denied, got 200")
} else {
t.Logf("Regular user correctly denied create role, code: %d", resp.Code)
}
})
}
// Test system role protection
func TestSystemRoleProtection(t *testing.T) {
ensureUsers(t)
// Try to modify system role (admin)
t.Run("Update System Role", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/roles/admin", map[string]string{
"name": "Hacked Admin",
}, AdminToken)
// Should fail - system roles are protected
if resp.Code == 200 {
t.Errorf("Expected system role update to be denied, got 200")
} else {
t.Logf("System role correctly protected, code: %d", resp.Code)
}
})
t.Run("Update System Role Permissions", func(t *testing.T) {
resp := doRequest(t, "PUT", "/api/roles/admin/permissions", map[string]interface{}{
"permission_ids": []string{},
}, AdminToken)
// Should fail - system roles are protected
if resp.Code == 200 {
t.Errorf("Expected system role permissions update to be denied, got 200")
} else {
t.Logf("System role permissions correctly protected, code: %d", resp.Code)
}
})
t.Run("Delete System Role", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/roles/admin", nil, AdminToken)
// Should fail - system roles cannot be deleted
if resp.Code == 200 {
t.Errorf("Expected system role deletion to be denied, got 200")
} else {
t.Logf("System role deletion correctly protected, code: %d", resp.Code)
}
})
}
Loading…
Cancel
Save