docs(test): swboot closeout evidence — executor e2e, L5 live PI, L6 playwright wizard
This commit is contained in:
@@ -44,11 +44,17 @@ SWBOOT-00..09 已合入并 L1–L4 验收;SWBOOT-10 为 fail-closed stub:`co
|
|||||||
## 三、计划
|
## 三、计划
|
||||||
|
|
||||||
1. [x] 分析(§一)
|
1. [x] 分析(§一)
|
||||||
2. [ ] 实现 §2.1(迁移/allowlist/executor/门禁/端点/设置键)+ 单测
|
2. [x] 实现 §2.1(goose 74/75、PG allowlist、executor、projectworkflow/catalog 门禁、server 端点)+ 单测(dev `4965e74d`,列名修复 `70f3028c`/`86e0cf46`)
|
||||||
3. [ ] 工程门禁 + 候选集成 dev
|
3. [x] 工程门禁 + 候选集成 dev(含 app→swboot 边界登记)
|
||||||
4. [ ] L5 复验 + L6 e2e 实跑(docker playwright)
|
4. [x] L5 复验(LiveMiniMax PASS 9.8s)+ L6 e2e 实跑(docker Playwright 1 passed)
|
||||||
5. [ ] 测试报告 + 矩阵行升 done + 收口清理
|
5. [ ] 测试报告 + 矩阵行升 done + 收口清理
|
||||||
|
|
||||||
## 四、结果登记
|
## 四、结果登记
|
||||||
|
|
||||||
(完成后回填)
|
- **review_state**: done(单测 9 项 + 工程门禁 + 既有契约更新)
|
||||||
|
- **integration_state**: done(dev `4965e74d`→`86e0cf46` 三轮候选合入)
|
||||||
|
- **deployment**: dev_runtime_verified(schema 75;SWBOOT-10 全链 + 三负例 + L5/L6)
|
||||||
|
- **验证结论**: W5 关闭条件全满足,见 [收口报告](../../test-reports/2026-09-26-swboot-closeout-test-report.md)
|
||||||
|
- **过程修复**: 绑定列名 source_ref(创建 500 残留 + 端点 409 误报)
|
||||||
|
- **cleanup**: 本任务 worktree 按门禁清理
|
||||||
|
- **remote_sync**: not_requested(本地 dev 前进,未 push)
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# workflow-swboot 收口:SWBOOT-10 非 stub + L5 真实 PI + L6 UI 二次验收 测试报告
|
||||||
|
|
||||||
|
## 一、 测试背景 (Background)
|
||||||
|
|
||||||
|
swboot 行剩余关闭条件:SWBOOT-10 非 stub(受控补丁执行器)+ L5 真实 PI + L6 UI 二次验收(W5 关闭条件,本轮用户指令授权)。此前 `controlled_patch` 双门禁拒绝、`repo_path_allowlist`/`project_source_bindings` 零运行时读写、`ExecuteControlledPatch/ApproveMerge` 恒拒。
|
||||||
|
|
||||||
|
## 二、 测试目标 (Test Objectives)
|
||||||
|
|
||||||
|
1. SWBOOT-10:DB allowlist(deny-all 默认)+ 隔离 worktree 补丁执行 + 管理员命令白名单 + 证据记录;merge/push/deploy 永不执行(类型化边界)
|
||||||
|
2. 门禁:`controlled_patch` 创建/预览仅在绑定 allowlist 仓库时放行(fail-closed 不变)
|
||||||
|
3. L5:真实 PI docs_only advisor 建议 `software_delivery` 且不自动激活
|
||||||
|
4. L6:Playwright 真实栈软件向导二次验收(含医疗去污染抽查)
|
||||||
|
|
||||||
|
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||||
|
|
||||||
|
- 单元:executor 真实临时 git 仓 6 用例;projectworkflow 门禁 1 用例;catalog 预览 1 用例;既有 stub 断言更新为边界契约
|
||||||
|
- 工程:`arch-check --mode block`(含 app→swboot 组合根边界登记)、`go vet`、`go test -short ./...`
|
||||||
|
- 运行时:no-docker dev(`86e0cf46`,schema 75 = goose 74/75 已应用);演示仓 /tmp/swboot-demo/repo
|
||||||
|
- L6:docker `mcr.microsoft.com/playwright:v1.63.0-noble` `--network host` 真实 dev 栈
|
||||||
|
- 证据:[assets/2026-09-26-swboot-closeout/](./assets/2026-09-26-swboot-closeout/)
|
||||||
|
|
||||||
|
## 四、 预期结果 (Expected Results)
|
||||||
|
|
||||||
|
| 用例 | 预期 |
|
||||||
|
|---|---|
|
||||||
|
| 预览/创建 controlled_patch(无绑定或未过 allowlist) | 拒绝(fail-closed 保持) |
|
||||||
|
| 预览/创建 controlled_patch(allowlist 绑定) | 放行 + `project_source_bindings` 落库 |
|
||||||
|
| 执行:补丁+白名单命令 | 201 succeeded;隔离分支;文件 hash+命令证据;仓库工作树不变 |
|
||||||
|
| 执行:非白名单命令/路径、非创建者 | 403/400;失败留证不留工作树 |
|
||||||
|
| merge | 恒拒(ERR_MERGE_REQUIRES_SEPARATE_AUTHORIZATION) |
|
||||||
|
| L5/L6 | 见 §二 3/4 |
|
||||||
|
|
||||||
|
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||||
|
|
||||||
|
### 5.1 单元/工程
|
||||||
|
|
||||||
|
- `go test ./internal/swboot/`:**8 项 PASS**(allowlist 拒绝 / 隔离落补丁+hash / 白名单命令执行+非白名单拒绝 / 坏补丁留证 / 命令失败即停 / merge 边界 / 预览门禁);`./internal/projectworkflow/` 门禁 PASS
|
||||||
|
- `arch-check`/`vet`/`-short` 全 PASS(候选 `4965e74d`、`70f3028c`、`86e0cf46`)
|
||||||
|
|
||||||
|
### 5.2 dev 运行时(真实 API,[runtime-evidence.txt](./assets/2026-09-26-swboot-closeout/runtime-evidence.txt))
|
||||||
|
|
||||||
|
- 种子 allowlist(/tmp/swboot-demo,test_commands=go,echo)→ 预览 valid → **创建 6638**:HTTP 201 `execution_mode=controlled_patch` 且绑定同批落库
|
||||||
|
- **执行成功**:HTTP 201 `status=succeeded`,隔离分支 `feature/swboot-p6635-*`,`main.go` sha256 证据,`echo demo-ok` exit 0;**仓库工作树保持 v1 未动**
|
||||||
|
- 负例:非白名单命令 403(未启动执行);非白名单路径 400 + 失败证据记录(无 worktree);非创建者 403
|
||||||
|
|
||||||
|
### 5.3 L5 真实 PI
|
||||||
|
|
||||||
|
`RUN_PI_E2E=1` LiveMiniMax **PASS**(9.8s):建议 `software_delivery` 并明确「禁止自动激活」——auto_activate=false 语义在当前 HEAD 成立。
|
||||||
|
|
||||||
|
### 5.4 L6 UI 二次验收
|
||||||
|
|
||||||
|
docker Playwright 真实栈 `swboot-wizard.spec.ts` **1 passed (1.2s)**:软件域切换 / solo 未独立评审横幅 / catalog「绿地立项」/ 必填三要素 / 创建进入项目 / 页面无「注册检验、GB 9706、器械分类」污染词。`playwright --list`:33 tests in 10 files(新 spec 已登记 README 矩阵 + npm 脚本)。
|
||||||
|
|
||||||
|
### 5.5 过程缺陷与修复
|
||||||
|
|
||||||
|
1. 绑定写入列名错误(`repo_path` → 实际列 `source_ref`):创建 500 且留下部分创建残留(项目 6635);修复后创建+绑定原子落库(6638 验证)。已知限制:绑定失败时项目行已插入(非同一事务),可用绑定端点补齐——登记后续增强。
|
||||||
|
2. 同类错误存在于执行端点绑定读取(409 误报);一并修复。
|
||||||
|
|
||||||
|
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||||
|
|
||||||
|
| 套件 | 结果 |
|
||||||
|
|---|---|
|
||||||
|
| `go test ./internal/swboot/ ./internal/projectworkflow/` | PASS |
|
||||||
|
| `arch-check --mode block` / `go vet` / `go test -short ./...`(候选) | PASS |
|
||||||
|
| dev 运行时 SWBOOT-10 全链 + 三负例 | PASS |
|
||||||
|
| L5 `RUN_PI_E2E=1` LiveMiniMax | PASS |
|
||||||
|
| L6 docker Playwright swboot-wizard | PASS |
|
||||||
|
|
||||||
|
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||||
|
|
||||||
|
**结论**:W5 关闭条件全满足——SWBOOT-10 以 fail-closed 受控执行器形态非桩化(DB allowlist / 隔离 worktree / 命令白名单 / 证据记录 / merge 永不执行),L5/L6 在当前 HEAD 真实链路 PASS。FEATURE-MATRIX 行可升 `done`。
|
||||||
|
|
||||||
|
**后续**:①创建+绑定同事务化;②执行记录管理台只读视图;③admin allowlist 维护 UI;④更广命令白名单形态(如按项目授予)。
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
== SWBOOT-10 dev runtime acceptance (dev 86e0cf46, no-docker runtime) ==
|
||||||
|
|
||||||
|
[allowlist seed] INSERT repo_path_allowlist ('/tmp/swboot-demo', test_commands='go,echo') -> 1 row
|
||||||
|
|
||||||
|
[creation preview] controlled_patch + repo_path=/tmp/swboot-demo/repo -> valid=true
|
||||||
|
[create 6638] HTTP 201 execution_mode=controlled_patch; project_source_bindings: 6638|/tmp/swboot-demo/repo|controlled_patch
|
||||||
|
|
||||||
|
[execution success] POST /projects/6635/swboot/executions {base_ref:HEAD, patch(main.go), commands:[echo demo-ok]}
|
||||||
|
-> HTTP 201 {"id":1,"status":"succeeded","branch":"feature/swboot-p6635-...","patches_applied":1,
|
||||||
|
"files":[{"path":"main.go","sha256":"02f108b..."}],"commands":[{"command":"echo demo-ok","exit_code":0,"output":"demo-ok"}]}
|
||||||
|
[isolation] repository working tree main.go remains println("v1"); patch only in isolated worktree
|
||||||
|
|
||||||
|
[negative: non-allowlisted command] curl -> 403 ERR_COMMAND_NOT_ALLOWLISTED (no execution started)
|
||||||
|
[negative: non-allowlisted repo] /etc -> 400, evidence record id=2 failed ERR_REPO_PATH_NOT_ALLOWLISTED (no worktree)
|
||||||
|
[negative: non-creator] lizhuguan -> 403 ERR_FORBIDDEN
|
||||||
|
|
||||||
|
== L5 real PI advisor (runner MiniMax-M2.7-highspeed) ==
|
||||||
|
RUN_PI_E2E=1 go test ./internal/app/ -run TestPiSoftwareBootstrapAdvisor_LiveMiniMax -> PASS (9.82s)
|
||||||
|
narrative: 建议 software_delivery + 禁止自动激活
|
||||||
|
|
||||||
|
== L6 UI wizard (docker mcr.microsoft.com/playwright:v1.63.0-noble, real dev stack :8080) ==
|
||||||
|
RUN_UI_ACCEPTANCE=1 docker ... test e2e/swboot-wizard.spec.ts --project=chromium --reporter=line
|
||||||
|
-> 1 passed (1.2s)
|
||||||
|
assertions: 软件域切换 / solo 未独立评审横幅 / catalog 绿地立项 / 必填三要素 / 创建进入项目 / 无医疗污染默认词
|
||||||
|
playwright --list: 33 tests in 10 files
|
||||||
+2
-1
@@ -12,10 +12,11 @@
|
|||||||
| `project143-regression.spec.ts` | `RUN_UI_ACCEPTANCE=1` | `npm run test:e2e:project143-regression` | 项目 143 只读 |
|
| `project143-regression.spec.ts` | `RUN_UI_ACCEPTANCE=1` | `npm run test:e2e:project143-regression` | 项目 143 只读 |
|
||||||
| `workflow-plan-409-frozen-duties.spec.ts` | `RUN_UI_ACCEPTANCE=1` | `npm run test:e2e:workflow-plan-409-frozen-duties` | |
|
| `workflow-plan-409-frozen-duties.spec.ts` | `RUN_UI_ACCEPTANCE=1` | `npm run test:e2e:workflow-plan-409-frozen-duties` | |
|
||||||
| `conversation-intent-dual-tab.spec.ts` | `INTENT_DUAL_TAB=1` | `npm run test:e2e:conversation-intent-dual-tab` | 独立双标签风险口径 |
|
| `conversation-intent-dual-tab.spec.ts` | `INTENT_DUAL_TAB=1` | `npm run test:e2e:conversation-intent-dual-tab` | 独立双标签风险口径 |
|
||||||
|
| `swboot-wizard.spec.ts` | `RUN_UI_ACCEPTANCE=1` | `npm run test:e2e:swboot-wizard` | SWBOOT L6 软件向导二次验收;需 project:create 账号 |
|
||||||
|
|
||||||
约定:
|
约定:
|
||||||
|
|
||||||
- `playwright test --list` 期望:**29 tests in 8 files**(Wave 3 退役冗余 `document-revision.spec.ts` / 消灭 `DOCREV_E2E_BASE_URL`)。
|
- `playwright test --list` 期望:**33 tests in 10 files**(Wave 3 退役冗余 `document-revision.spec.ts` / 消灭 `DOCREV_E2E_BASE_URL`)。
|
||||||
- 新增 spec 必须同时补本表与 `package.json` 脚本。
|
- 新增 spec 必须同时补本表与 `package.json` 脚本。
|
||||||
- 不升 `MULTI_INTENT_PLAN_TIER`;不触碰项目 143 写路径。
|
- 不升 `MULTI_INTENT_PLAN_TIER`;不触碰项目 143 写路径。
|
||||||
- DOCREV 收口分组(依赖 UI-00.. 前置,不可安全单独中途开跑):`npm run test:ui-acceptance:groups` → UI-03 / UI-05 / UI-06 / UI-07-08-09 / UI-11 / UI-13 / post-restart。
|
- DOCREV 收口分组(依赖 UI-00.. 前置,不可安全单独中途开跑):`npm run test:ui-acceptance:groups` → UI-03 / UI-05 / UI-06 / UI-07-08-09 / UI-11 / UI-13 / post-restart。
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { test, expect, type Page } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SWBOOT L6 UI 二次验收(W5 关闭条件):软件向导在真实 dev 栈上的完整冒烟。
|
||||||
|
* 与其他 e2e 一致:仅 RUN_UI_ACCEPTANCE=1 时运行(无 mock),需要
|
||||||
|
* ACCEPTANCE_USERNAME / ACCEPTANCE_PASSWORD(具备 project:create 的账号)。
|
||||||
|
* 断言:纯软件域切换、solo 未评审横幅、catalog 类型、必填三要素、
|
||||||
|
* 创建成功进入项目、页面无医疗污染默认词(设计 §11.4)。
|
||||||
|
*/
|
||||||
|
const runAcceptance = process.env.RUN_UI_ACCEPTANCE === '1';
|
||||||
|
const USER = process.env.ACCEPTANCE_USERNAME?.trim() || '';
|
||||||
|
const PASS = process.env.ACCEPTANCE_PASSWORD?.trim() || '';
|
||||||
|
|
||||||
|
test.skip(!runAcceptance, 'set RUN_UI_ACCEPTANCE=1 against the real dev stack');
|
||||||
|
|
||||||
|
async function login(page: Page, username: string, password: string) {
|
||||||
|
await page.goto('/');
|
||||||
|
await page.fill('input[placeholder*="系统用户名"]', username);
|
||||||
|
await page.fill('input[placeholder*="登录密码"]', password);
|
||||||
|
await page.click('button:has-text("进入医疗协同工作台")');
|
||||||
|
await expect(page.getByText('新建立项会话')).toBeVisible({ timeout: 30_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
test('swboot wizard: software domain, solo banner, catalog type, create project', async ({ page }) => {
|
||||||
|
if (!USER || !PASS) {
|
||||||
|
throw new Error('ACCEPTANCE_USERNAME / ACCEPTANCE_PASSWORD are required');
|
||||||
|
}
|
||||||
|
await login(page, USER, PASS);
|
||||||
|
|
||||||
|
await page.click('button:has-text("新建立项会话")');
|
||||||
|
await expect(page.getByText('发起医疗器械新项目立项')).toBeVisible({ timeout: 10_000 });
|
||||||
|
|
||||||
|
await page.click('[data-testid="create-domain-software"]');
|
||||||
|
await expect(page.getByText('发起软件项目')).toBeVisible();
|
||||||
|
|
||||||
|
// 切到 solo:未独立评审横幅(W5a 契约)
|
||||||
|
const teamSelect = page.locator('select', { has: page.locator('option[value="solo"]') });
|
||||||
|
await teamSelect.selectOption('solo');
|
||||||
|
await expect(page.locator('[data-testid="solo-unreviewed-banner"]')).toBeVisible();
|
||||||
|
|
||||||
|
// 服务端 catalog 项目类型可选
|
||||||
|
const typeSelect = page.locator('[data-testid="software-project-type"]');
|
||||||
|
await expect(typeSelect).toBeVisible();
|
||||||
|
const options = await typeSelect.locator('option').allTextContents();
|
||||||
|
expect(options.join('|')).toContain('绿地立项');
|
||||||
|
|
||||||
|
// 必填软件三要素 + 项目名称
|
||||||
|
const suffix = Date.now().toString().slice(-6);
|
||||||
|
await page.fill('input[placeholder="例如:无线便携式双频超声骨动力微创手术系统"]', `swboot-ui-${suffix}`);
|
||||||
|
await page.fill('input[placeholder="问题陈述"]', '手工回归成本高,需要自举工具链');
|
||||||
|
await page.fill('input[placeholder="目标用户"]', '平台工程团队');
|
||||||
|
await page.fill('input[placeholder="目标结果"]', '一条命令完成环境自举');
|
||||||
|
|
||||||
|
await page.click('button:has-text("确认立项并创建会话")');
|
||||||
|
await expect(page.getByText(`swboot-ui-${suffix}`).first()).toBeVisible({ timeout: 30_000 });
|
||||||
|
|
||||||
|
// 软件项目页面不出现医疗污染默认词(抽查)
|
||||||
|
const body = await page.locator('body').innerText();
|
||||||
|
for (const banned of ['注册检验', 'GB 9706', '器械分类']) {
|
||||||
|
expect(body, `software page must not default to medical term ${banned}`).not.toContain(banned);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"test:ui-acceptance:post-restart": "RUN_UI_ACCEPTANCE=1 UI_PHASE=post-restart playwright test e2e/document-revision-runtime.spec.ts --project=chromium -g @post-restart",
|
"test:ui-acceptance:post-restart": "RUN_UI_ACCEPTANCE=1 UI_PHASE=post-restart playwright test e2e/document-revision-runtime.spec.ts --project=chromium -g @post-restart",
|
||||||
"test:ui-acceptance:groups": "node --import tsx -e \"import { DOCREV_UI_RERUN_GROUPS } from './src/lib/playwrightPhaseArtifacts.ts'; for (const g of DOCREV_UI_RERUN_GROUPS) console.log([g.id, g.phase, g.grep, g.cases.join(',')].join('\\t'))\"",
|
"test:ui-acceptance:groups": "node --import tsx -e \"import { DOCREV_UI_RERUN_GROUPS } from './src/lib/playwrightPhaseArtifacts.ts'; for (const g of DOCREV_UI_RERUN_GROUPS) console.log([g.id, g.phase, g.grep, g.cases.join(',')].join('\\t'))\"",
|
||||||
"test:e2e:admin-console": "RUN_UI_ACCEPTANCE=1 playwright test e2e/admin-console.spec.ts --project=chromium",
|
"test:e2e:admin-console": "RUN_UI_ACCEPTANCE=1 playwright test e2e/admin-console.spec.ts --project=chromium",
|
||||||
|
"test:e2e:swboot-wizard": "RUN_UI_ACCEPTANCE=1 playwright test e2e/swboot-wizard.spec.ts --project=chromium",
|
||||||
"test:e2e:controlled-document-reload": "RUN_UI_ACCEPTANCE=1 playwright test e2e/controlled-document-reload.spec.ts --project=chromium",
|
"test:e2e:controlled-document-reload": "RUN_UI_ACCEPTANCE=1 playwright test e2e/controlled-document-reload.spec.ts --project=chromium",
|
||||||
"test:e2e:conversation-intent-dual-tab": "INTENT_DUAL_TAB=1 playwright test e2e/conversation-intent-dual-tab.spec.ts --project=chromium",
|
"test:e2e:conversation-intent-dual-tab": "INTENT_DUAL_TAB=1 playwright test e2e/conversation-intent-dual-tab.spec.ts --project=chromium",
|
||||||
"test:e2e:governance-action-level-authz": "RUN_UI_ACCEPTANCE=1 playwright test e2e/governance-action-level-authz.spec.ts --project=chromium",
|
"test:e2e:governance-action-level-authz": "RUN_UI_ACCEPTANCE=1 playwright test e2e/governance-action-level-authz.spec.ts --project=chromium",
|
||||||
|
|||||||
Reference in New Issue
Block a user