Test Plan: Spec 3 — AP 套用流程(Flow Template Binding)

產出日期:2026-05-13 對應 specdesign.md v2.1, implementation-plan.md v1.1 Branchfeature/project-flow-engine-integrate HEAD(plan commit)c0e26bd 跨 repo 範圍:BE (compliance-manager-be) + FE (compliance-manager-fe) + E2E (compliance-manager-test)


0. 文件定位與分工

本 test-plan 不重複 implementation-plan.md 內已展開的 unit test bodies(M2/M3/M4 each task 都已有 pytest -v 範例 + assertion)。本文件補的是:

範圍 來源
Acceptance criteria 拆解 design.md §五需求摘要 + §六 in-scope + §十一 error handling
Traceability matrix AC ↔︎ test case 本文件 §5
整合測試 / lifecycle / 隔離 case 補強 本文件 §3
效能驗證(N+1 防範) 本文件 §3.5
回歸驗證(Spec 2 不被破壞) 本文件 §3.6
FE E2E BDD scenarios(含 Phase E fixture 重做) 本文件 §4
Manual smoke checklist(DB SQL + curl 範例) 本文件 §6
負向 / RLS / DI missing scenario 本文件 §3.7

implementation-plan.md 內已有的 unit test(M2.1 FlowTemplateEnricher 4 tests、M2.2 batch query 3 tests、M3.1–M3.5 carry-forward 5 service tests、M4.1–M4.4 service + integration tests)以「引用」方式列入追溯矩陣,不重貼程式碼。


1. Acceptance Criteria 清單

從 design.md §五(需求摘要 5 條)+ §六 in-scope(11 條 BE/FE 改動)+ §十一 error handling(6 條情境)拆出可驗證條件。

1.1 核心需求(design.md §五)

AC ID 條件 來源
AC-01 建專案時 user 可選流程範本,AP1 (第一輪) 自動綁該範本 snapshot design.md §五.1 / §七
AC-02 新增稽核計畫 (新一輪 AP) 時 user 可選流程範本,dropdown default 帶最新一輪 AP 的 master uid design.md §五.2 / §10.2
AC-03 AP 建立同時 clone master BPMN 成 per-AP workflow_template snapshot row(engine 表) design.md §五.3 / §八 data flow
AC-04 後續修改 / 軟刪 master 不影響進行中 AP 的 snapshot.xml(snapshot 不可變) design.md §五.4 / §四
AC-05 多 AP 互不影響 — 兩 AP 即使選同一 master,也是各自獨立 snapshot row + 獨立 workflow_execution design.md §五.5 / §四

1.2 BE in-scope(design.md §六 / §九)

AC ID 條件 來源
AC-06 POST /oscal/projects/start request schema 含 flow_template_uid: UUID (required);缺欄位回 400 design.md §9.5 / §十一
AC-07 POST /grc/project/<uid>/launch-new-round request schema 同上 design.md §9.5 / §十一
AC-08 _bind_main_workflow_to_ap 簽章改為接 flow_template_uid: str (required),拔除 default lookup design.md §9.3
AC-09 launch_new_round 補做 main workflow snapshot(呼叫 _bind_main_workflow_to_ap)— Spec 2 漏蓋 design.md §三 / §9.2
AC-10 FlowTemplateDomainService.DEFAULT_MAIN_WORKFLOW_TEMPLATE_NAME / get_default_main_workflow_template() / IFlowTemplateRepo.get_builtin_by_name() 全刪 design.md §9.4
AC-11 GET /grc/project/<uid>/assessment-plans/menu(列表)回 flow_template: {snapshot_uid, master_uid, master_name, is_master_active} design.md §9.6
AC-12 GET /grc/project/<uid>/ap/<ap_uid>(詳情)同上 enrichment 欄位 design.md §9.6
AC-13 列表 enrichment 為 batch(三段 query:ext / engine / master),不出現 N+1 design.md §9.6 效能段
AC-14 Enrichment lenient 行為:master 軟刪 → is_master_active=false 不擋 API;ext 缺 → flow_template=null 不擋 API design.md §9.6
AC-15 GRC_FLOW_MAIN_TEMPLATE_NOT_FOUND (412022) error code 刪除 design.md §9.7
AC-16 GRC_AR_DATA_MISSING 編號從 500001 改 412024 design.md §9.7

1.3 FE in-scope(design.md §十)

AC ID 條件 來源
AC-17 ProjectCreateView 含流程範本 dropdown + 📋 詳細選擇按鈕 + 預覽 Dialog(reuse FlowTemplatePreviewDialog.vue design.md §10.1 / §10.3
AC-18 ProjectCreateView 流程範本必填驗證;未選時送出顯示錯誤訊息 design.md §10.1 vee-validate rule
AC-19 ProjectApListView「新增稽核計畫」Dialog 含 dropdown + 預覽 + 沿用上一輪預設 hint design.md §10.2 / §10.5 i18n
AC-20 i18n key project.flowTemplate.* zh-tw / en 兩語系皆補齊 design.md §10.5

1.4 Error handling(design.md §十一)

AC ID 條件 來源
AC-21 flow_template_uid → 400 (webargs schema) design.md §十一
AC-22 flow_template_uid 非 UUID 格式 → 400 design.md §十一
AC-23 UUID 形式正確但 master 不存在 / 軟刪 (is_active=false) → 404 GRC_FLOW_TEMPLATE_NOT_FOUND design.md §十一
AC-24 Cross-tenant master uid → 404(RLS 自動 filter) design.md §十一
AC-25 Engine snapshot 或 start_workflow_execution 失敗 → 整 @transaction rollback,不留半成品 AP design.md §十一
AC-26 DI 缺失(_flow_template_domain_service_workflow_template_snapshot_service 是 None)→ 412 GRC_FLOW_BINDING_DI_MISSING implementation-plan.md M4.4 §Step 3 註解

AC 總數 = 26 條


2. 測試金字塔(範圍 & 工具)

工具 位置 範圍
BE Unit pytest + unittest.mock compliance-manager-be/test/test_*.py Service / domain / repo / enricher / handler / precondition
BE Integration(API + DB) pytest + Flask test client + 真 DB compliance-manager-be/test/test_*_api.py Route → service → repo → ORM → DB (RLS) lifecycle
FE 單元(Vitest) Vitest compliance-manager-fe/src/**/__tests__/* Dropdown component / form validation(非本 plan 主軸,列入但不展開)
E2E BDD Cucumber.js + Playwright compliance-manager-test/features/regression/project-v2/* 完整使用者旅程(建專案 → AP1 lifecycle → 建新一輪 → AP2 lifecycle)
Manual Smoke curl + psql (本機) M7 必過 checklist(design.md §12.3)

3. BE 測試(pytest)

路徑:compliance-manager-be/test/ 帳號:blsit / Billows@123!(pytest 用,conftest.py 已設好 app_client / blsit_headers) Response 格式:{"status": true/false, "data": {...}}(注意:分頁 meta 在頂層、非 data 內)

3.1 Unit Tests(plan 已展開,引用即可)

Test File 來源 plan task Tests 數 覆蓋 AC
test_flow_template_enricher.py M2.1 4 AC-11, AC-13, AC-14
test_flow_template_domain_service.py (補 batch) M2.2 3 AC-13
test_ap_extension_domain_service.py (補 batch) M2.2 2 AC-13
test_workflow_template_service.py (補 batch) M2.2 1 AC-13
test_workflow_template_snapshot_service.py M3.1 3 AC-03, AC-05
test_stage_advance_service.py M3.2 ≥3 (regression)
test_oscal_stage_preconditions.py M3.3 ≥6 (3 fn × 2) (regression)
test_oscal_stage_handlers.py M3.4 ≥6 (3 handler × 2) (regression)
test_assessment_plan_extension_repo_impl.py M3.5 4 AC-09
test_oscal_project_service_flow_template.py M4.1, M4.4 4 AC-06, AC-08, AC-09, AC-23

小計:≥36 個 unit test 案例(plan 已展開 bodies;本 plan 不重貼)

3.2 整合測試補強(本 plan 新增)

Test File Test Method 目的 覆蓋 AC
test_post_oscal_projects_start_flow_template.py test_post_201_creates_ap1_with_correct_snapshot start_oscal_project 帶 valid uid → API 200 + ap_extensions 一列 + snapshot row 一列 + workflow_execution 一列 AC-01, AC-03, AC-06
同檔 test_post_400_missing_flow_template_uid 缺欄位 → webargs schema 擋 400 AC-06, AC-21
同檔 test_post_400_invalid_uuid_format uid 非 UUID 格式 → 400 AC-22
同檔 test_post_404_template_not_found uid 隨機 → 404 GRC_FLOW_TEMPLATE_NOT_FOUND AC-23
同檔 test_post_404_template_soft_deleted uid 對應 master is_active=false → 404 AC-23
同檔 test_post_404_cross_tenant_template uid 屬於 tenant B,user 在 tenant A → 404(RLS) AC-24
同檔 test_post_rolls_back_when_engine_fails mock start_workflow_execution raise → AP / ext / snapshot 都不留 AC-25
test_post_launch_new_round_flow_template.py test_launch_new_round_creates_ap2_independent_snapshot 同 project 建第二輪 AP → snapshot uid 不同 / workflow_execution uid 不同 AC-02, AC-05, AC-09
同檔 test_launch_new_round_400_missing_flow_template_uid 缺欄位 → 400 AC-07, AC-21
同檔 test_launch_new_round_404_template_soft_deleted 軟刪 master uid → 404 AC-23
同檔 test_launch_new_round_rolls_back_on_engine_failure helper 中段失敗 → AP1 ext 不變、AP2 完全不留 AC-25
test_grc_assessment_plan_api_enrichment.py test_menu_returns_flow_template_for_every_ap 列表 N 個 AP → N 個 flow_template payload,欄位齊全 AC-11
同檔 test_menu_returns_null_when_ext_missing 舊資料 ext 不存在 → flow_template=null,API 仍 200 AC-14
同檔 test_menu_returns_is_master_active_false_when_soft_deleted master 軟刪 → 欄位齊全但 is_master_active=false AC-14
同檔 test_detail_returns_flow_template 詳情 endpoint 同 enrichment AC-12
同檔 test_menu_uses_batch_queries_no_n_plus_1 5 個 AP → 三段 query 各只觸發 1 次(用 sqlalchemy.event listen before_cursor_execute 計數,或 mock domain service assert_called_once AC-13
test_grc_error_code_uniqueness.py (擴) test_grc_flow_main_template_not_found_removed grep GRC_FLOW_MAIN_TEMPLATE_NOT_FOUNDgrc_error_code.py 不存在 AC-15
同檔 test_grc_ar_data_missing_code_is_412024 GrcErrorCode.GRC_AR_DATA_MISSING == ("...", "GRC_412024") AC-16

小計:18 個新整合測試

3.3 Lifecycle 端到端 pytest(最重要的補強)

模擬 manual smoke 的 SQL 驗證自動化版,BE 內全程跑通。

test_spec3_lifecycle.py

Test Method 目的 覆蓋 AC
test_full_lifecycle_two_rounds_two_templates 建專案選範本 A → 推 AP1 4 stage 到 closed → launch_new_round 選範本 B → 推 AP2 3 stage 到 closed(B 跳 poam)→ 驗 DB:兩列 ext / 兩列 snapshot row / 兩 workflow_execution 互不引用 AC-01, AC-02, AC-03, AC-05, AC-09
test_snapshot_immutable_after_master_xml_change 建 AP1 → 直接 SQL UPDATE compliance.flow_templates SET bpmn_xml = '...modified...' WHERE name='A' → 重查 public.workflow_templates snapshot row → xml 仍是原 build 時內容 AC-04
test_snapshot_survives_master_soft_delete 建 AP1 → UPDATE flow_templates SET is_active=false WHERE name='A' → AP1 推進 stage 仍正常運作;但 POST /oscal/projects/start 再選同 uid → 404 AC-04, AC-23
test_two_aps_same_master_get_different_snapshots 建專案選 A → 再 launch_new_round 又選 A → 兩 ap_extensions 行的 flow_template_snapshot_uid 不同 AC-05
test_launch_new_round_default_value_path 建 AP1 選 A → 呼叫 GET /grc/project/<uid>/assessment-plans/menu → response data[0].flow_template.master_uid == A.uid(FE default 帶值依賴此) AC-02, AC-11

小計:5 個 lifecycle 級 integration test

3.4 Tenant 隔離 / RLS(補強)

test_spec3_rls_isolation.py

Test Method 目的 覆蓋 AC
test_cross_tenant_master_returns_404 Tenant A user 用 tenant B 的 flow_template_uid → 404(RLS 自動 filter,與「master 不存在」同碼,不漏資訊) AC-24
test_cross_tenant_ap_list_does_not_enrich Tenant A user 撈 tenant B project 的 AP list → RLS 直接擋 project 查詢(不會進入 enricher) AC-24

小計:2 個 RLS test

3.5 效能驗證(N+1 防範)

test_flow_template_enricher_perf.py

Test Method 方法 覆蓋 AC
test_enrich_many_n_aps_three_queries_max sqlalchemy.event.listen('before_cursor_execute') 計 SQL 次數;建 10 個 AP → enrich 應只觸發 ≤3 個 SELECT(一段 ext / 一段 engine / 一段 master) AC-13
test_menu_api_query_count_constant_in_ap_count 整 API call 跑 N=1 / N=5 / N=20 → enrichment 段 SQL 數量不隨 N 線性成長 AC-13

實作備註:若 sqlalchemy event hook 太重,退而求其次用 mock domain_service.get_many_by_uids.assert_called_once() 驗 batch path(plan M2.1 Step 2 第 4 個 test case 已用此 pattern)。

小計:2 個效能 test

3.6 回歸驗證(Spec 2 不被破壞)

test_spec2_regression_under_spec3.py

Test Method 目的 覆蓋
test_single_round_full_lifecycle_still_works 建專案選範本 A → AP1 推 4 stage 到 closed(不建新一輪)→ Spec 2 既有功能不退化 Spec 2 regression
test_stage_advance_uses_per_ap_snapshot_not_master AP1 推進時,BPMN parsing 從 snapshot.xml 撈 next node(非 master.xml)— 改 master 不影響推進邏輯 AC-04, Spec 2 regression
test_remove_default_lookup_no_caller_left grep 整個 codebase 確認 get_default_main_workflow_template / DEFAULT_MAIN_WORKFLOW_TEMPLATE_NAME / get_builtin_by_name 無 hit AC-10

Test 3 用 subprocess 跑 grep -rn 並 assert 0 hit,當作 lint-style guard。

小計:3 個 regression test

3.7 負向 / Edge case 補強

test_spec3_negative_cases.py

Test Method 目的 覆蓋 AC
test_di_missing_returns_412 mock OscalProjectService 三個 dep 中任一為 None,直接 invoke _bind_main_workflow_to_ap → 412 GRC_FLOW_BINDING_DI_MISSING AC-26
test_master_has_empty_bpmn_xml master.bpmn_xml = '' → clone 應 raise(engine start_workflow_execution 會炸 BPMN parser),整 transaction rollback AC-25
test_concurrent_launch_new_round_race 同一 project 兩個 request 同時 launch_new_round(threading 或 pytest-asyncio)→ 至少一個成功、另一個應該有明確錯誤(不能 silent corrupt) edge case
test_old_round1_ap_with_no_ext_still_appears_in_list 模擬升級前的舊資料 — AP1 沒對應 ext row → menu API 仍回該 AP,flow_template=null AC-14

小計:4 個 negative test

BE 測試總計

類別 數量
Unit (plan 已展開引用) ≥36
Integration 補強 18
Lifecycle 5
RLS 2
效能 2
回歸 3
Negative / edge 4
BE 總計 ≥70 個 test case

4. FE E2E 測試(Cucumber + Playwright)

路徑:~/Projects/Billows/Audit-Manager/compliance-manager-test/ 技術:Cucumber.js + Playwright + Node.js(BDD 三層分離:feature → step → page object) 範圍:本 spec 涉及修改 + 新增的 scenarios 一律在 compliance-manager-test/不在 BE 或 FE 主專案內加 e2e。

4.1 既有 fixture 修補(Phase E 4 bugs,對應 plan M8)

來源:docs/issues/pending/2026-05-13-test-fixture-broken-by-spec2-phase-d-and-fe-refactor.md 本 spec 加 dropdown 後,wizard 結構變更,既有 fixture 必須適配。

Fixture / Method 檔案 修補內容 阻擋 scenario
ProjectCreatePage.addFourParticipantsWithRoles pages/project-v2/ProjectCreatePage.js 適配「基本資訊」step 新增的流程範本 dropdown — 確認 step 順序、page.click('[data-test=step-next]') 數量、locator selector 01-project-create.feature 大部分
launchProjectViaUi pages/project-v2/ProjectOverviewPage.js(或 helper) M4 補齊 main workflow binding 後,preparing → active 推進按鈕應該不再 disabled;驗 await expect(button).toBeEnabled() 07-project-overview.feature Banner scenarios
launchAuditViaUi 同上 active → auditing 推進;同上邏輯 同上
launchRemediationViaUi 同上 auditing → remediation 推進 09-poam.feature 入口

4.2 Feature 檔案新增 / 修改

4.2.1 新增 features/regression/project-v2/01-project-create.feature(加 scenario)

@regression @project-v2 @create @flow-template @P0
Scenario: 建專案時必須選擇流程範本
  Given 我以專案管理者身份登入系統
  And 我前往建立專案頁面
  When 我在 Step0 填寫基本資訊但不選流程範本
  And 我點擊下一步
  Then 應顯示「請選擇流程範本」必填錯誤訊息
  And 頁面應停留在 Step0

@regression @project-v2 @create @flow-template @P0
Scenario: 建專案選擇「完整稽核流程」範本並完成建立
  Given 我以專案管理者身份登入系統
  And 我前往建立專案頁面
  When 我在 Step0 填寫基本資訊並選擇流程範本「完整稽核流程」
  And 我完成 Step1–Step3 流程
  And 我點擊建立專案按鈕
  Then 應出現成功 Toast 且導航至專案總覽頁面
  And 第一輪 AP 的 flow_template.master_name 應為「完整稽核流程」

@regression @project-v2 @create @flow-template @P1
Scenario: 預覽 Dialog 顯示流程範本詳細階段
  Given 我以專案管理者身份登入系統
  And 我前往建立專案頁面
  When 我在 Step0 選擇流程範本「完整稽核流程」
  And 我點擊「📋 詳細選擇」按鈕
  Then 應彈出流程範本預覽 Dialog
  And Dialog 應顯示 BPMN 圖以及階段清單(規劃 / 執行 / 稽核 / 改善 / 結束)

對應 step / page

Step file 新增 step Page object method
steps/project-v2/project-create.steps.js When 我在 Step0 填寫基本資訊並選擇流程範本 {string} ProjectCreatePage.fillStep0WithTemplate(name)
同上 When 我點擊「📋 詳細選擇」按鈕 ProjectCreatePage.openFlowTemplatePreview()
同上 Then Dialog 應顯示 BPMN 圖以及階段清單 ... ProjectCreatePage.assertPreviewDialogContent()

4.2.2 新增 / 修改 features/regression/project-v2/04-project-planning.feature

「稽核計畫頁新增稽核計畫 Dialog」對應的 page object 是 ProjectApListPage.js

@regression @project-v2 @planning @flow-template @P0
Scenario: 新增稽核計畫 Dialog 流程範本必填
  Given 我以專案管理者身份登入系統
  And 我前往專案稽核計畫頁
  When 我點擊「新增稽核計畫」
  Then Dialog 應開啟,且流程範本 dropdown 預設帶最新一輪 AP 的範本
  When 我手動清空流程範本選擇並送出
  Then 應顯示「請選擇流程範本」必填錯誤

@regression @project-v2 @planning @flow-template @P0
Scenario: 新增稽核計畫選不同範本建立第二輪 AP
  Given 我以專案管理者身份登入系統
  And 我已建立第一輪 AP 並選擇範本「完整稽核流程」並推進至 closed
  And 我前往專案稽核計畫頁
  When 我點擊「新增稽核計畫」
  And 我改選流程範本「自我評估稽核」
  And 我填寫計畫名稱並送出
  Then 應出現成功 Toast
  And AP 列表應包含兩列,且其 flow_template.master_name 分別為「完整稽核流程」與「自我評估稽核」

@regression @project-v2 @planning @flow-template @P1
Scenario: 新增稽核計畫 dropdown default 帶最新一輪範本
  Given 我以專案管理者身份登入系統
  And 專案已有兩輪 AP,最新一輪使用範本「自我評估稽核」
  When 我點擊「新增稽核計畫」
  Then Dialog dropdown 預設應為「自我評估稽核」
  And 應顯示「預設沿用上一輪稽核計畫的流程範本」提示

對應 step / page

Step file 新增 step Page object method
steps/project-v2/project-planning.steps.js When 我點擊「新增稽核計畫」 ProjectApListPage.openNewRoundDialog()
同上 Then Dialog dropdown 預設應為 {string} ProjectApListPage.assertNewRoundDefaultTemplate(name)
同上 When 我改選流程範本 {string} ProjectApListPage.selectFlowTemplate(name)
同上 Then 應顯示「預設沿用上一輪稽核計畫的流程範本」提示 ProjectApListPage.assertInheritedHintVisible()

4.2.3 新增 features/regression/project-v2/10-spec3-full-lifecycle.feature(端到端 lifecycle)

整合 4.2.1 + 4.2.2 + 推進 stage 的最完整旅程;可被視為 spec 3 P0 smoke 自動化版(呼應 design.md §12.3 / plan M7)。

Feature: Spec 3 — AP 套用流程完整 lifecycle
  作為專案管理者,我希望兩個 AP 能各自綁定不同流程範本且互不干擾

  @regression @project-v2 @spec3 @P0 @lifecycle
  Scenario: 建專案 A (範本「完整稽核流程」) → 推 4 stage → 建新一輪 (範本「自我評估稽核」) → 推 3 stage
    Given 我以專案管理者身份登入系統
    # Round 1 — 建專案 + 4 stage 推進
    When 我建立專案並選擇流程範本「完整稽核流程」
    Then 第一輪 AP Banner 應顯示 stage 為「規劃」
    When 我推進 stage「啟動專案」
    Then AP1 狀態應為 active 且 Banner stage 為「執行」
    When 我推進 stage「啟動稽核」
    Then AP1 狀態應為 auditing 且 Banner stage 為「稽核」
    When 我推進 stage「提交稽核」
    Then AP1 狀態應為 remediation 且 Banner stage 為「改善」
    When 我推進 stage「完成改善」
    Then AP1 狀態應為 closed
    # Round 2 — 建新一輪 + 3 stage 推進(跳 poam)
    When 我新增稽核計畫並選擇流程範本「自我評估稽核」
    Then 第二輪 AP Banner 應顯示 stage 為「規劃」
    When 我推進 stage「啟動專案」
    Then AP2 狀態應為 active
    When 我推進 stage「啟動稽核」
    Then AP2 狀態應為 auditing
    When 我推進 stage「提交稽核」
    Then AP2 狀態應為 closed
    # Snapshot 隔離驗證(透過 API 比對)
    Then AP1 與 AP2 的 flow_template.snapshot_uid 應不同
    And AP1 與 AP2 的 flow_template.master_uid 應分別為兩個不同範本

對應新 step

Step file Step Page object
steps/project-v2/spec3-lifecycle.steps.js(新建) When 我建立專案並選擇流程範本 {string} reuse ProjectCreatePage.fillStep0WithTemplate + 推進至 Step4 提交
同上 When 我推進 stage {string} ProjectOverviewPage.advanceStage(buttonLabel)
同上 Then AP1 與 AP2 的 flow_template.snapshot_uid 應不同 helper 呼叫 BE API GET /grc/project/<uid>/assessment-plans/menu 比對

4.3 Page Object 新增 / 修改清單

Page Object 路徑 修改 / 新增 method
ProjectCreatePage pages/project-v2/ProjectCreatePage.js + fillStep0WithTemplate(templateName);+ openFlowTemplatePreview();+ assertPreviewDialogContent();改 addFourParticipantsWithRoles 適配新 step
ProjectApListPage pages/project-v2/ProjectApListPage.js + openNewRoundDialog();+ selectFlowTemplate(name);+ assertNewRoundDefaultTemplate(name);+ assertInheritedHintVisible()
ProjectOverviewPage pages/project-v2/ProjectOverviewPage.js + advanceStage(buttonLabel);驗證 Banner stage code 與 AP status;reuse for stage 推進 scenarios
BasePage(如需) pages/BasePage.js 若需共用 BE API helper 撈 ext / snapshot uid,加 fetchAssessmentPlansMenu(projectUid)

4.4 E2E 測試總計

類別 數量
Project create scenarios(新增) 3
Project planning scenarios(新增) 3
Spec 3 full lifecycle scenario 1
Phase E fixture 修補(影響既有 scenarios) 4 (Bug A/B/C/D)
E2E 新增 scenarios 7 scenarios

5. Traceability Matrix(AC ↔︎ Test Case)

AC ID 條件 BE Unit BE Integration E2E Manual Smoke 備註
AC-01 建專案選範本 → AP1 綁正確 snapshot M4.4 unit (test_oscal_project_service_flow_template) test_post_201_creates_ap1_with_correct_snapshot / test_full_lifecycle_two_rounds_two_templates 建專案選「完整稽核流程」scenario §6 Step 2
AC-02 新一輪 AP dropdown default 帶最新範本 test_launch_new_round_default_value_path dropdown default scenario §6 Step 4 FE-heavy,BE 只保證 enrichment 對
AC-03 per-AP snapshot clone M3.1 (test_clone_master_as_snapshot_returns_new_uid) test_post_201_creates_ap1_with_correct_snapshot (透過 lifecycle 間接覆蓋) §6 Step 6 DB 驗
AC-04 改 master 不影響進行中 AP test_snapshot_immutable_after_master_xml_change / test_snapshot_survives_master_soft_delete / test_stage_advance_uses_per_ap_snapshot_not_master §6 Step 7 E2E 不重複(pytest 覆蓋足)
AC-05 多 AP 互不影響 M3.1 (test_clone_two_times_returns_different_uids) test_two_aps_same_master_get_different_snapshots / test_launch_new_round_creates_ap2_independent_snapshot full lifecycle scenario ... snapshot_uid 應不同 §6 Step 6 DB 驗
AC-06 start_oscal_project 必填 uid M4.1 (test_start_oscal_project_raises_when_flow_template_uid_empty) test_post_400_missing_flow_template_uid create scenario「必須選擇流程範本」
AC-07 launch_new_round 必填 uid test_launch_new_round_400_missing_flow_template_uid planning scenario「流程範本必填」
AC-08 _bind_main_workflow_to_ap 簽章重做 M4.4 unit tests test_post_201_creates_ap1_with_correct_snapshot
AC-09 launch_new_round 補綁 main workflow(Spec 2 漏蓋) M3.5 (test_assessment_plan_extension_repo_impl) test_launch_new_round_creates_ap2_independent_snapshot full lifecycle scenario §6 Step 5(AP2 Banner 不再 disabled)
AC-10 拔除 default lookup hardcode test_remove_default_lookup_no_caller_left(grep guard)
AC-11 AP 列表 enrichment M2.1 (test_enrich_returns_full_payload_when_all_layers_exist) test_menu_returns_flow_template_for_every_ap §6 Step 6 curl menu
AC-12 AP 詳情 enrichment test_detail_returns_flow_template §6 Step 6 curl detail
AC-13 Batch query 防 N+1 M2.1 (test_enrich_uses_batch_queries) + M2.2 batch tests test_menu_uses_batch_queries_no_n_plus_1 / test_enrich_many_n_aps_three_queries_max / test_menu_api_query_count_constant_in_ap_count
AC-14 Lenient enrichment M2.1 (test_enrich_returns_none_when_ext_missing / test_enrich_marks_master_inactive_when_soft_deleted) test_menu_returns_null_when_ext_missing / test_menu_returns_is_master_active_false_when_soft_deleted / test_old_round1_ap_with_no_ext_still_appears_in_list
AC-15 412022 刪除 test_grc_flow_main_template_not_found_removed grep 確認
AC-16 500001 → 412024 test_grc_ar_data_missing_code_is_412024
AC-17 FE create dropdown + 預覽 create scenario「選擇『完整稽核流程』範本」+「預覽 Dialog 顯示」 §6 Step 2
AC-18 FE create dropdown 必填驗證 create scenario「必須選擇流程範本」 §6 Step 2
AC-19 FE planning dropdown + default planning scenario「dropdown default 帶最新一輪」 §6 Step 4
AC-20 i18n 兩語系 ⚠️ E2E 未覆蓋(單 locale 跑) manual 切英文目視 feature-development-workflow.md 預設不跑 i18n 切換 E2E
AC-21 缺欄位 400 test_post_400_missing_flow_template_uid / test_launch_new_round_400_missing_flow_template_uid (necessary subset of FE scenarios)
AC-22 非 UUID 格式 400 test_post_400_invalid_uuid_format
AC-23 master 不存在 / 軟刪 404 M4.1 invalid uid test test_post_404_template_not_found / test_post_404_template_soft_deleted / test_launch_new_round_404_template_soft_deleted
AC-24 Cross-tenant 404 test_post_404_cross_tenant_template / test_cross_tenant_master_returns_404 / test_cross_tenant_ap_list_does_not_enrich RLS 自動,pytest 需建 2 tenant fixture
AC-25 Engine 失敗 rollback test_post_rolls_back_when_engine_fails / test_launch_new_round_rolls_back_on_engine_failure / test_master_has_empty_bpmn_xml mock + 驗 DB
AC-26 DI missing 412 test_di_missing_returns_412

覆蓋率小結:26 條 AC,25 條有自動化測試覆蓋;AC-20(i18n 兩語系)僅 manual smoke(feature-development-workflow 預設不跑 i18n E2E)。


6. Manual Smoke Checklist(必過項,呼應 plan M7)

用途:BE + FE 都 commit 完後跑一次端到端,當 lifecycle pytest 漏抓的最後一道網。 帳號:blsadmin / Billows@123! 環境:本機 BE port 8000 + FE port 5173 + DB 192.168.50.188:25432 / guidant_ai_dev

Step 1 — 起環境

# BE
cd ~/Projects/Billows/Audit-Manager/compliance-manager-be
pkill -9 -f main_socketio.py
python main_socketio.py > /dev/null 2>&1 &
tail -f log/app.log &  # 另開 terminal 監看

# FE
cd ~/Projects/Billows/Audit-Manager/compliance-manager-fe
npm run dev

Step 2 — 建專案 A(範本「完整稽核流程」)

# 驗 BE response
curl -s -H "Authorization: Bearer <token>" -H "X-Tenant-ID: <tid>" \
  http://localhost:8000/grc/project/<proj_uid>/assessment-plans/menu \
  | jq '.data[0].flow_template'
# Expect: {snapshot_uid, master_uid, master_name: "完整稽核流程", is_master_active: true}

Step 3 — AP1 推 4 stage 到 closed

Step 4 — launch_new_round(範本「自我評估稽核」)

Step 5 — AP2 推 3 stage 到 closed

Step 6 — DB 驗(cmmgr 帳號連線)

-- 連線
psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev
-- 密碼 jedi@123!

-- 抓 project / ap id
SELECT p.id AS pid, ap.id AS apid, ap.title
FROM compliance.projects p JOIN oscal.assessment_plans ap ON ap.project_id = p.id
WHERE p.uid = '<project_uid>'
ORDER BY ap.id;
-- 預期:兩 row,ap1 / ap2

-- 兩列 ap_extensions,flow_template_snapshot_uid 不同
SELECT assessment_plan_id, flow_template_snapshot_uid, workflow_execution_uid
FROM compliance.assessment_plan_extensions
WHERE assessment_plan_id IN (<ap1_id>, <ap2_id>);
-- Expect: 2 rows,snapshot_uid 互不相同,workflow_execution_uid 也不同 ✓

-- 兩列 snapshot
SELECT id, uid, name, source_template_uid, provider, version
FROM public.workflow_templates
WHERE uid IN (
  SELECT flow_template_snapshot_uid FROM compliance.assessment_plan_extensions
  WHERE assessment_plan_id IN (<ap1_id>, <ap2_id>)
);
-- Expect: 2 rows,provider='snapshot',source_template_uid 分別對 master A/B uid

-- 兩個 workflow_executions
SELECT uid, name, template_id, status
FROM public.workflow_executions
WHERE uid IN (
  SELECT workflow_execution_uid FROM compliance.assessment_plan_extensions
  WHERE assessment_plan_id IN (<ap1_id>, <ap2_id>)
);
-- Expect: 2 rows,名稱 AP-<ap1uid>-main / AP-<ap2uid>-main,互不引用

Step 7 — Snapshot 隔離驗證

-- 模擬 admin 改 master A 的 BPMN
UPDATE compliance.flow_templates
SET bpmn_xml = '<bpmn:definitions xmlns:bpmn="..."><!-- modified --></bpmn:definitions>'
WHERE name = '完整稽核流程';

-- 驗 snapshot row 不變
SELECT xml FROM public.workflow_templates WHERE uid = '<ap1_snapshot_uid>';
-- Expect: 仍是 build 時的原 XML,xml 字串中沒有 'modified'
-- 模擬軟刪 master B
UPDATE compliance.flow_templates SET is_active = false WHERE name = '自我評估稽核';

-- AP2 推進若仍進行中應正常(snapshot 仍在);但 List enrichment 應顯示 is_master_active=false
# 驗 enrichment lenient
curl -s -H "Authorization: ..." http://localhost:8000/grc/project/<proj_uid>/assessment-plans/menu \
  | jq '.data[] | {ap: .title, ft: .flow_template}'
# Expect: AP2.flow_template.is_master_active == false(其他欄位齊全),API 不擋

Step 8 — 負向 manual

Step 9 — 若任一驗證 fail

按 CLAUDE.md「重大決策完成後自動歸檔思考過程」開 docs/analysis/2026-05-13-spec3-smoke-failure-root-cause.md,並到 changelog 補一筆 fix(如根因落在 Spec 2 service → 同時補 M3 5 個 service test gap)。


7. 執行順序與閘門

┌─────────────────────────────────────────────────────────────────────────┐
│  M0 Pre-flight grep verify                                              │
└─────────────────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  M1 → M2 → M3 (BE)                                                      │
│  - M1 error code 整理                                                    │
│  - M2 enrichment + 4 unit tests + 2 integration tests 必綠               │
│  - M3 carry-forward 5 service test gap 必綠(保 M4 安全網)              │
└─────────────────────────────────────────────────────────────────────────┘
                            │  ◀── 閘門 A:M1+M2+M3 pytest 全綠
                            ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  M4 BE Core (簽章 + helper 改造 + 拔 hardcode)                          │
│  - §3.2 18 個 integration test + §3.3 5 個 lifecycle test 必綠           │
│  - §3.6 regression 3 test 必綠(Spec 2 不退化)                          │
└─────────────────────────────────────────────────────────────────────────┘
                            │  ◀── 閘門 B:BE 端 ≥70 test 全綠
                            ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  M5 + M6 FE                                                              │
│  - Vitest 元件測試(如有)+ FE manual smoke 跑得通                       │
└─────────────────────────────────────────────────────────────────────────┘
                            │  ◀── 閘門 C:FE smoke 通主流程
                            ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  M7 Manual Smoke Checklist (§6 Step 1-9)                                │
└─────────────────────────────────────────────────────────────────────────┘
                            │  ◀── 閘門 D:8 個 step 全 ✓ 才能 merge
                            ▼
┌─────────────────────────────────────────────────────────────────────────┐
│  M8 (事後) Phase E fixture 修補 + E2E §4.2 7 個 scenario 全綠            │
└─────────────────────────────────────────────────────────────────────────┘
                            │  ◀── 閘門 E:E2E 7 scenario + 4 fixture bug 修完
                            ▼
                       Spec 3 task arc 收尾
                  (changelog 收口 / conversation history / summary)

閘門政策

  • 閘門 A 失敗 → 不進 M4(避免改 service 找不到 regression 來源)
  • 閘門 B 失敗 → 不進 FE(避免 FE 接到壞 API)
  • 閘門 D 失敗 → 不 merge;按 §6 Step 9 開 analysis + 補 test
  • 閘門 E 是「事後追補」,不阻 spec 3 main task arc merge,但要在 issue 追蹤

8. 不在本次測試範圍

明確列出,避免無限擴張:

  • 跨瀏覽器相容(Chrome only;Firefox/Safari 由現有 regression suite 涵蓋)
  • i18n 切換測試(AC-20 只 manual 目視;不寫切英文跑全套 E2E)
  • 壓力測試 / load test(spec 沒要求;snapshot clone 是 O(1) 不會出問題)
  • 安全測試(XSS / SQLi)(marshmallow + SQLAlchemy parameterized 自動防護)
  • FE 元件單元測試完整鋪(FE 沒有 vitest baseline;只在新增的 dropdown wrapper 加 1 個 vitest happy path 就好)
  • 「中途調整 AP 流程」UI(design.md §六 out-of-scope,未來 spec)
  • 跨 tenant 範本共享(design.md §六 out-of-scope;v1 純 tenant 隔離)
  • 舊資料 backfill(design.md M4 changelog 註:dev DB 既有 round-1 AP 已有效運作,不 backfill)

9. 風險與待釐清

風險 影響 緩解
WorkflowTemplateServiceget_many_by_uids 是 jedi-flow-engine 套件異動 套件 API 變動需 path mode dev + 發版前 bump M2.2 plan 已標:dev 走 path mode,feature 完才 bump(user 確認)
M3 carry-forward 5 service test 若揭露 Spec 2 既存 bug M4 改動會踩到 → smoke 過不去 M3 順序在 M4 前;fail 就先停下處理
AP list enrichment N+1 在 RLS 下無法用 sqlalchemy.event 計準 query 數 §3.5 效能 test 假陽 退到 mock domain service assert_called_once pattern(M2.1 已示範)
DB sequence 權限漏給 cm_app(CLAUDE.md SQL Migration 規範) 新建 table 寫入 silent fail 本 spec 無新 table,零風險;但若 M3 補 carry-forward test 揭露 ext table 權限漏 → 補 GRANT
Phase E 4 fixture bug 修不完 M8 拖延,BDD scenario 跑不起來 M8 標為「事後追補」不阻 M1-M7 merge;但 issue 追蹤直到 close
FE dropdown loading 時 user 已點下一步 dropdown 為空時表單可能誤判已填 M5 加 :loading="loadingTemplates";空 list 時必填驗證仍擋
AC-25 rollback 測試難以模擬 engine 失敗 mock engine service raise 即可,但要確認 SQLAlchemy nested transaction 行為 已在 test_post_rolls_back_when_engine_fails 用 monkeypatch + 事後查 DB 0 row 驗證
AC-20 i18n 兩語系僅 manual 覆蓋 翻譯 typo 不被自動化抓到 接受風險;i18n 文字以 PR review 為主防線

10. 完成檢查(DoD)


11. 文件版本

版本 日期 變更
v1 2026-05-13 初版 — 對應 design.md v2.1 + implementation-plan.md v1.1;26 條 AC;≥70 BE test + 7 E2E scenarios + Phase E 4 fixture bug 修補