template-monorepo/test/e2e/permission_test.go

271 lines
9.2 KiB
Go
Raw Normal View History

2026-05-21 23:52:39 +00:00
//go:build e2e
package e2e
import (
"encoding/json"
"fmt"
"net/http"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestPermission_Catalog(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-01", "GET", "/api/v1/permissions/catalog", "讀全平台 Permission Catalog 樹狀結構")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/catalog?tree=true", nil, true)
var data struct {
Tree []map[string]any `json:"tree"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.NotEmpty(t, data.Tree)
}
func TestPermission_Me(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-02", "GET", "/api/v1/permissions/me", "讀當前 user 的角色 + 權限樹")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/me?include_tree=true", nil, true)
var data struct {
UID string `json:"uid"`
TenantID string `json:"tenant_id"`
Roles []string `json:"roles"`
Permissions map[string]string `json:"permissions"`
Tree []map[string]any `json:"tree"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.Equal(t, c.Fixture.UID, data.UID)
require.Equal(t, c.Fixture.TenantID, data.TenantID)
require.Contains(t, data.Roles, c.Fixture.RoleKey)
require.NotEmpty(t, data.Permissions)
}
func TestPermission_RoleCRUD(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-03~P-06", "*", "/api/v1/permissions/roles", "租戶角色 CRUD建立 → 列表 → 更新 display_name → 刪除")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_custom_role",
"display_name": "E2E Custom",
"status": "open",
}, true)
var role struct {
ID string `json:"id"`
Key string `json:"key"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.Equal(t, "e2e_custom_role", role.Key)
require.NotEmpty(t, role.ID)
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
// 避免 e2e-up 反覆跑時 role 殘留 → 後續 Create 撞 unique key
t.Cleanup(func() { _, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true) })
2026-05-21 23:52:39 +00:00
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/roles", nil, true)
var list struct {
Roles []struct {
Key string `json:"key"`
} `json:"roles"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, r := range list.Roles {
if r.Key == "e2e_custom_role" {
found = true
break
}
}
require.True(t, found, "created role should appear in list")
patchEnv := c.DoExpectOK(t, http.MethodPatch, "/api/v1/permissions/roles/"+role.ID, map[string]string{
"display_name": "E2E Custom Renamed",
}, true)
var patched struct {
DisplayName string `json:"display_name"`
}
require.NoError(t, json.Unmarshal(patchEnv.Data, &patched))
require.Equal(t, "E2E Custom Renamed", patched.DisplayName)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_RolePermissions(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-07/P-08", "PUT/GET", "/api/v1/permissions/roles/:id/permissions", "Role 全量替換 Permission含 parent closure再讀回比對")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
permissionID := firstCatalogPermissionID(t, c)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_role_permissions",
"display_name": "E2E Role Permissions",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.NotEmpty(t, role.ID)
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
t.Cleanup(func() { _, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true) })
2026-05-21 23:52:39 +00:00
c.DoExpectOK(t, http.MethodPut, "/api/v1/permissions/roles/"+role.ID+"/permissions", map[string][]string{
"permission_ids": {permissionID},
}, true)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/roles/"+role.ID+"/permissions", nil, true)
var list struct {
Permissions []struct {
ID string `json:"id"`
} `json:"permissions"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
require.NotEmpty(t, list.Permissions)
found := false
for _, p := range list.Permissions {
if p.ID == permissionID {
found = true
break
}
}
require.True(t, found, "role should include requested permission")
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_AssignUserRole(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-09~P-11", "*", "/api/v1/permissions/users/:uid/roles", "User ↔ Role 指派 / 列表 / 撤銷")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_assign_role",
"display_name": "E2E Assign",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
t.Cleanup(func() {
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles/"+role.ID, nil, true)
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
})
2026-05-21 23:52:39 +00:00
c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles", map[string]string{
"role_id": role.ID,
}, true)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles", nil, true)
var list struct {
UserRoles []struct {
RoleID string `json:"role_id"`
} `json:"user_roles"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, r := range list.UserRoles {
if r.RoleID == role.ID {
found = true
break
}
}
require.True(t, found)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles/"+role.ID, nil, true)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_RoleMappingCRUD(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-12", "*", "/api/v1/permissions/role-mappings", "外部 IdP group → 內部 Role.Key 對映 CRUD")
2026-05-21 23:52:39 +00:00
c := NewClient(t)
roleKey := "e2e_mapping_role"
externalKey := fmt.Sprintf("e2e-group-%s", c.Fixture.UID)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": roleKey,
"display_name": "E2E Mapping Role",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.NotEmpty(t, role.ID)
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
t.Cleanup(func() {
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
})
2026-05-21 23:52:39 +00:00
upsertEnv := c.DoExpectOK(t, http.MethodPut, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
"internal_role_key": roleKey,
}, true)
var mapping struct {
ID string `json:"id"`
ExternalSource string `json:"external_source"`
ExternalKey string `json:"external_key"`
InternalRoleID string `json:"internal_role_id"`
InternalRoleKey string `json:"internal_role_key"`
}
require.NoError(t, json.Unmarshal(upsertEnv.Data, &mapping))
require.NotEmpty(t, mapping.ID)
require.Equal(t, "zitadel", mapping.ExternalSource)
require.Equal(t, externalKey, mapping.ExternalKey)
require.Equal(t, role.ID, mapping.InternalRoleID)
require.Equal(t, roleKey, mapping.InternalRoleKey)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/role-mappings?source=zitadel", nil, true)
var list struct {
Mappings []struct {
ExternalKey string `json:"external_key"`
} `json:"mappings"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, item := range list.Mappings {
if item.ExternalKey == externalKey {
found = true
break
}
}
require.True(t, found, "created role mapping should appear in list")
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_CasbinRBAC(t *testing.T) {
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
e2eStep(t, "P-13/P-14", "*", "/api/v1/permissions/{policy/reload,roles}", "Casbin enforcementowner reload policy → no-role user GET /roles=403")
2026-05-21 23:52:39 +00:00
if os.Getenv("E2E_CASBIN") != "1" {
t.Skip("set E2E_CASBIN=1 and use e2e.casbin.yaml to enable Casbin enforcement")
}
owner := NewClient(t)
reloadEnv := owner.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/policy/reload", nil, true)
var reload struct {
Tenant string `json:"tenant"`
TS int64 `json:"ts"`
}
require.NoError(t, json.Unmarshal(reloadEnv.Data, &reload))
require.Equal(t, owner.Fixture.TenantID, reload.Tenant)
require.Positive(t, reload.TS)
noRole := NewNoRoleClient(t)
denied := noRole.DoExpectHTTP(t, http.MethodGet, "/api/v1/permissions/roles", nil, true, http.StatusForbidden)
require.NotEqual(t, int64(successCode), denied.Code)
}
func firstCatalogPermissionID(t *testing.T, c *Client) string {
t.Helper()
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/catalog?tree=false", nil, true)
var data struct {
List []struct {
ID string `json:"id"`
} `json:"list"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.NotEmpty(t, data.List)
require.NotEmpty(t, data.List[0].ID)
return data.List[0].ID
}