12 KiB
前端 E2E 冒烟 · Sidecar 本地冒烟 · CI 构建矩阵
对应验收矩阵(
docs/product/plan-completion-matrix.md):
- 第 118 行「黄金测试是发布门禁」剩余项——完整前端 E2E、干净离线机安装冒烟、CI 构建矩阵;
- 第 99 行「自包含 Python Sidecar」剩余本地可测项——20 次冷启动、端口冲突、崩溃重启、中文/空格路径。
本轮交付的本地可测部分全部落地并跑通;签名 / SBOM 上链 / Defender / 干净离线机安装 属外部环境项, 需签名证书、Defender 门户与离线验证机,见文末「剩余项与门禁说明」。
1. 前端 E2E 冒烟(Playwright)
1.1 运行前提
- Node ≥ 22(仓库根
package.jsonengines),后端 8003 已运行(jms认证,登录zhangzhen / sz@zz@1116,企业platform)。 - 首次运行先装浏览器二进制(约 150 MB,需网络):
cd apps/web
npm run test:e2e:install # npx playwright install chromium
1.2 一键运行(本地门禁)
cd apps/web
npm run test:e2e # = npm run build && playwright test(preview 模式,推荐)
- 默认 preview 模式:
npm run build产出dist/后由 Playwright 起vite preview(独立端口 43117), 并通过VITE_API_TARGET=http://127.0.0.1:8003把/api代理到本地后端。 - 为什么用 preview 而不是 dev:preview 走生产构建,行为最接近发布包,作为 E2E 默认门禁。
round-41 发现的 dev 模式 StrictMode 缺陷(
refreshWorld结果捕获 +alive存活标记被 StrictMode 模拟卸载永久置 false,导致world/resultStore更新被吞、视口不出现)已在 round-42 修复: ①aliveeffect 体内复位(mount→cleanup→mount 后保持 true);②captureSessionResult移出setWorld更新器(纯函数化,经worldRef计算合并世界)。dev 模式(E2E_MODE=dev)已作为附加 冒烟验证通过;E2E 默认仍为 preview,CI 亦然。 E2E_MODE=dev仅作本地调试用:Playwright 会自动复用已运行的 5173(reuseExistingServer)。
1.3 覆盖清单(中文断言)
| 用例 | 断言 |
|---|---|
| 访客冒烟·品牌与登录入口 | .sidebar-brand 含「工业智核」;登录触发按钮可用 |
| 访客冒烟·登录弹窗 | 弹窗标题「登录工业智核」;企业/账号/密码三字段;Esc 可关闭 |
| 核心路径(JMS) | 登录 platform/zhangzhen → 输入「跑一版柔性排产」→ 智能体中文回复非空 → .flex-block-title 含「柔性排产」→ 结果入口可用 → 视口打开(页签含「世界对比」,点击后 .world-diff 可见)→ 右侧「柔性工作台」打开 .flex-bench → Ctrl+K 命令面板:中文过滤「世界」命中「两个世界对比」、过滤「柔性」命中「柔性工作台」,Esc 关闭 |
1.4 常用变量
| 环境变量 | 默认 | 说明 |
|---|---|---|
E2E_MODE |
preview |
preview(build 后 vite preview,默认门禁)或 dev(vite dev;round-42 起 dev StrictMode 缺陷已修复,可作附加冒烟) |
E2E_FRONTEND_PORT |
43117(preview)/ 5173(dev) |
前端端口(dev 模式复用已运行 5173) |
E2E_API_TARGET |
http://127.0.0.1:8003 |
/api 代理目标(CI 指向矩阵自起后端端口) |
E2E_BASE_URL |
http://localhost:<port> |
覆盖前端地址 |
E2E_OUTPUT_DIR |
系统临时目录 | 截图/追踪产物目录(默认不污染仓库) |
APS_E2E_SKIP_JMS |
空 | 1 时跳过核心路径(无外网 JMS 场景),只跑离线访客冒烟 |
E2E_JMS_TENANT/USER/PASSWORD |
platform / zhangzhen / sz@zz@1116 |
JMS 登录参数 |
1.5 离线/无外网回退
- 若运行器无法访问 JMS(
https://jms-mes-api.jzplat.com),设APS_E2E_SKIP_JMS=1: 核心路径自动 skip,访客冒烟(2 例,不依赖后端)仍作为最小离线冒烟门禁。 - 浏览器二进制无法下载时:仍需已装 Chromium 才能跑访客冒烟;Chromium 二进制下载域名
cdn.playwright.dev需可达。
2. Sidecar 本地冒烟(矩阵 99 本地部分)
node scripts/smoke-sidecar-local.mjs
只读调用 server/sidecar.py(python -m server.sidecar)与桌面 Sidecar 生命周期语义,不修改后端逻辑;
每次启动都使用隔离的临时 APS_HOME / APS_DB_PATH / APS_KNOWLEDGE_PATH / APS_APPROVAL_PATH / APS_WORLD_PATH,
不触碰仓库 server/data 或 ~/.aps。任一检查失败退出码非 0。
| 检查 | 预期 |
|---|---|
| 20 次冷启动 | 随机端口 + 随机 nonce 起/停,健康探针(nonce 校验 200 + 回显头)通过 |
| 端口冲突 | 预占端口后启动 → 显式失败(退出码非 0,stderr 含 WinError 10048 / address in use) |
| 崩溃重启 | kill sidecar 子进程后父进程(脚本模拟)以新端口+新 nonce 拉起新实例 |
| 父进程看门狗 | 父进程退出后 sidecar 由守护线程自行退出(exit 0) |
| 中文/空格路径 | 在含中文+空格的 cwd / APS_HOME / APS_UI_DIR 下启动,健康 + UI 均正常,无 Traceback |
可选 APS_SIDECAR_PYTHON=<python> 指定解释器;默认 .sidecar-venv → .venv → PATH。
「干净 Win10/11 离线机安装冒烟」需外部验证机,不在本地冒烟范围。
3. CI 构建矩阵(.github/workflows/ci.yml)
push / pull_request 触发,5 个 job:
| Job | 平台 | 内容 |
|---|---|---|
lint |
Ubuntu | ruff check server tests scripts;informational(基线 55 项存量告警,round-41 记录;清零后改硬门禁) |
frontend-build |
Windows + Ubuntu | apps/web:npm ci → npm run build(tsc + vite) |
backend-tests |
Windows + Ubuntu | pip install -r requirements.txt pytest → pytest tests/golden -q;APS_KNOWLEDGE_PATH 等全部隔离到 runner.temp |
e2e |
Ubuntu | 起后端(独立端口 8100、隔离数据、jms)→ npm ci + build → playwright install --with-deps chromium → npx playwright test(preview 模式);失败自动上传 aps-e2e-artifacts |
sbom |
Ubuntu | pip install uv 提供 uvx → npm run sbom → 校验 dist/sbom/*.cdx.json 与 release-manifest.json |
CI 变量/密钥:
APS_E2E_SKIP_JMS(仓库变量,可选):1时 E2E 只跑离线访客冒烟。APS_JMS_SESSION_SECRET(仓库密钥,可选):JMS 会话签名密钥;未配置时用内置 CI 回退值。- JMS 登录依赖运行器可访问
https://jms-mes-api.jzplat.com;若企业侧 IP 白名单限制 GitHub 出口,按上条降级。
3.5 数据隔离(C-P0 fail-closed)
核心路径 E2E 会在目标后端创建真实会话(会话标题=首条消息,如「柔性排产」)。
apps/web/playwright.config.ts 因此默认拒绝 127.0.0.1:8003 / localhost:8003:该端口是现场开发后端,
直接复用会污染当前用户的项目、会话和排产结果。
唯一支持的本地启动方式
在仓库根目录新开 PowerShell,完整执行下面这一段;不要删减路径变量,也不要复用已经运行的 8003:
$root = Join-Path ([System.IO.Path]::GetTempPath()) ("aps-e2e-" + [guid]::NewGuid().ToString("N"))
$home = Join-Path $root "home"
$data = Join-Path $home "data"
New-Item -ItemType Directory -Force -Path $data | Out-Null
$env:APS_HOME = $home
$env:APS_DATA_DIR = $data
$env:APS_DB_PATH = Join-Path $data "master.db"
$env:APS_WORLD_PATH = Join-Path $data "world.json"
$env:APS_PROJECTS_PATH = Join-Path $home "sessions\workspace.json"
$env:APS_KNOWLEDGE_PATH = Join-Path $data "knowledge.json"
$env:APS_CHECKPOINT_PATH = Join-Path $data "checkpoints.json"
$env:APS_PREFERENCE_PATH = Join-Path $data "preferences.json"
$env:APS_EMBEDDINGS_PATH = Join-Path $data "embeddings.json"
$env:APS_APPROVAL_PATH = Join-Path $data "approvals.json"
$env:APS_APPROVAL_BACKEND = "file"
$env:APS_AUDIT_LEDGER_DIR = Join-Path $data "audit-ledger"
$env:APS_AUDIT_MIRROR_DIR = Join-Path $data "audit-mirror"
$env:APS_AUTOMATION_STATE_PATH = Join-Path $data "automation.json"
$env:APS_MCP_BUS_PATH = Join-Path $data "mcp-bus.json"
$env:APS_BRANCH_PATH = Join-Path $data "branches.json"
$env:APS_BRANCH_DIR = Join-Path $data "branches"
$env:APS_GOLDEN_CACHE = Join-Path $data "golden-tests.json"
Remove-Item Env:APS_DATABASE_URL -ErrorAction SilentlyContinue
# R71.1 图纸 E2E 要求显式开启测试认证,避免仓库 .env 免登录配置让登录按钮永不启用
$env:APS_AUTH_ENABLED = "1"
.\.venv\Scripts\python.exe -X utf8 -m uvicorn server.main:app --host 127.0.0.1 --port 8100
后端健康后,在第二个 PowerShell 中运行:
cd apps/web
$env:E2E_API_TARGET = "http://127.0.0.1:8100"
Remove-Item Env:E2E_ALLOW_LIVE_BACKEND -ErrorAction SilentlyContinue
npm run test:e2e
CI 同样使用独立端口 8100 和 runner.temp 持久化路径。
现场 8003 的显式例外
只有在操作者明确接受“向当前现场工作区创建真实会话”时,才允许:
$env:E2E_API_TARGET = "http://127.0.0.1:8003"
$env:E2E_ALLOW_LIVE_BACKEND = "1"
npm run test:e2e
未设置 E2E_ALLOW_LIVE_BACKEND=1 时,Playwright 在加载配置阶段直接失败,不会启动浏览器或发送请求。
4. 门禁达成与剩余项
已达成(本地门禁,均实测通过)
- 按 §3.5 启动 8100 隔离后端后,
cd apps/web && npm run test:e2e:3/3 通过(登录 → 排产 → 结果块 → 视口/工作台 → 命令面板;中文断言)。 node scripts/smoke-sidecar-local.mjs:5/5 通过,退出码 0。- CI 矩阵定义落地(Windows+Ubuntu 后端黄金测试与前端构建、E2E、SBOM)。
剩余(需外部环境,非本轮可测)
- 干净离线 Win10/11 安装冒烟——需无网验证机执行 NSIS 安装包安装/卸载/首次启动(矩阵 99/118)。
- 代码签名——需受信任代码签名证书;当前
release-manifest.json明确signed: false,仅为本地完整性清单。 - Defender 扫描入 CI 门禁——需 Defender for Endpoint 门户/API 与提交策略。
- SBOM 上链/签名 attestation——当前
npm run sbom产出 CycloneDX 1.6 + SHA-256 清单,但未做签名与供应链上链。
前端缺陷状态(round-42 已修复):dev 模式 StrictMode 下 refreshWorld / captureSessionResult
状态更新被吞的问题已修复——根因 ① alive ref 存活标记:effect 只写 cleanup 置 false,StrictMode
mount→cleanup→mount 双调用后永久为 false,refreshWorld 的全部提交被跳过;修复为 effect 体内复位 true。
根因 ② captureSessionResult 在 setWorld 更新器内调用(更新器非纯函数、dev 双调用重复执行);修复为
更新器外经 worldRef 计算 merged 后提交。dev 模式核心路径已 E2E 冒烟通过,preview 默认门禁保持 3/3。
5. CP RHS 参数重算浏览器验收
- 打开“设置 -> 算法 Skill -> 异步任务”,选择开始日期。
- 保留 C8=60 分钟、班组/工装容量=1,费率留空并提交
CP RHS 增量重算。 - 轮询完成后打开结果:C8 显示全部待排订单;C12 仅显示真实激活资源的 code/id;未接线参数显示 inactive,不启动变体重解。
- 无费率时每行只显示“未配置”,不得出现 0、CNY 或估算成本。再显式填写费率提交一次,确认成本按对应增量计算,同时页面声明目标改善与货币成本不可直接相减。
- OPTIMAL 行显示精确改善;FEASIBLE 行只显示区间。C8 若出现“恢复可行”必须作为可行性异常,而不是收益。
- 验收记录必须包含
/api/jobs请求/轮询结果、浏览器 console warning/error、新增任务取消、8003/5173 或隔离端口健康状态;不得在浏览器验收中使用生产业务数据做写操作。
5.1 C7 line/day 实例
- 进入异步任务后,先选开始日期,再选择 ACTIVE 产线和 C7 工作日;输入增加分钟,费率留空提交。
- 结果必须显示具体
lineCode/#lineId + bucketDate、baselineRhs → perturbedRhs,并声明开工日整笔记账、非物化班次模型。 - 无费率时 C7 货币字段全 null/显示未配置。仅填写 C7 实例费率后重算,成本只能出现在该 line/day 行,单位为每产线/工作日增量分钟。
- DatePicker 必须禁用所选产线的非工作日;伪造非工作日、未启用产线、越界日期或非法实例时仍须在创建线程前 422,任务总数不增加。
- 浏览器网络中不得出现发布/下发请求;最终 console warning/error 为 0。