# Nexus 全站测试用例矩阵（SSOT）

> 编号规则：`<层>-<域>-<序号>`  
> 层：`UT` 单元 · `IT` 集成 · `CH` 链条 · `E2E` Playwright · `MCP` Agent 浏览器  
> 状态：`✅` 已有 · `🔲` 待写 · `🟡` 部分覆盖 · `⏭` 不验收（已确认豁免）

最后更新：2026-06-09

---

## 0. 横切能力

| ID | 层 | 用例 | 前置 | 步骤 | 期望 | 状态 | 实现位置 |
|----|-----|------|------|------|------|------|----------|
| UT-AUTH-001 | UT | JWT access 过期边界 | 固定 clock | 签发 exp-1s token | 401 + 中文提示 | ✅ | test_login_access.py |
| UT-AUTH-002 | UT | refresh 复用检测 | 双 refresh 同 jti | 第二次 refresh | 401 revoke 全家 | ✅ | test_auth_refresh_reuse.py |
| UT-VAL-001 | UT | 422 loc 中文化 | — | per_page=999 | detail 含 `每页条数` | ✅ | test_validation_errors_zh.py |
| UT-TIME-001 | UT | 北京时间格式化 | UTC 输入 | format_display_time | `Asia/Shanghai` 字符串 | ✅ | test_display_time.py |
| UT-SEC-001 | UT | 路径穿越拒绝 | — | `../etc/passwd` | ValidationError | ✅ | test_remote_path_validation.py |
| IT-AUTH-001 | IT | 登录换 token | API 活 | POST /api/auth/login | access+refresh | ✅ | test_api.py |
| IT-AUTH-002 | IT | 无 token 403 | — | GET /api/servers | 401/403 | ✅ | test_api.py |
| CH-AUTH-001 | CH | 登录→刷新→访问 | 活 API | login→refresh→GET servers | 200 | ✅ | tests/chain/test_auth_flow.py |
| E2E-AUTH-001 | E2E | 错误密码 | 浏览器 | 填错密码登录 | 页内错误提示 | ✅ | e2e/pages/auth.spec.mjs |
| MCP-AUTH-001 | MCP | 登录页渲染 | base URL | snapshot #/login | 表单+品牌可见 | ✅ | e2e/pages/login.spec.mjs |

---

## 1. 安装向导 `/app/install.html`

| ID | 层 | 用例 | 期望 | 状态 | 备注 |
|----|-----|------|------|------|------|
| UT-INST-001 | UT | 已安装锁 404 | /api/install/* 404 | ✅ | test_install_locked_404.py |
| IT-INST-001 | IT | 无 .env 仅 install 路由 | /health 与 /api/servers 行为 | ✅ | tests/integration/test_install_mode.py |
| E2E-INST-001 | E2E | 安装页可打开 | install.html 200 | ✅ | e2e/pages/install.spec.mjs |
| MCP-INST-001 | MCP | 安装步骤 UI | 各 step 控件可见 | ⏭ | **不验收**：生产已锁安装向导；E2E-INST-001 可达性已够 |

---

## 2. 仪表盘 Dashboard `#/`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| IT-DASH-001 | IT | GET /api/dashboard/stats | 200 + 数字字段 | ✅ | tests/integration/test_servers_dashboard.py |
| E2E-DASH-001 | E2E | 仪表盘加载 | 卡片/图表区域可见 | ✅ | full-acceptance #2 + dashboard-stats.spec.mjs |
| MCP-DASH-001 | MCP | 统计数字非空 | 在线/告警计数 | ✅ | 沉淀 E2E-DASH-001 |

---

## 3. 服务器 Servers `#/servers`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-SRV-001 | UT | 列表排序键合法 | sort 白名单 | ✅ | test_server_list_sort.py |
| UT-SRV-002 | UT | status 字段枚举 | online/offline/… | ✅ | test_server_status_field.py |
| UT-SRV-003 | UT | target_path 过滤 | 非法路径拒绝 | ✅ | test_server_target_path_filter.py |
| IT-SRV-001 | IT | GET /api/servers 分页 | total+items | ✅ | test_api.py |
| IT-SRV-002 | IT | per_page>200 → 422 中文 | loc_zh | ✅ | 422 改造后 |
| IT-SRV-003 | IT | notify 开关 settings API | 审计+CUD | ✅ | tests/integration/test_notify_settings.py |
| CH-SRV-001 | CH | 列表→详情抽屉→保存 | DB 与响应一致 | ✅ | tests/chain/test_server_update_flow.py |
| E2E-SRV-001 | E2E | 表格+筛选 | 表头+搜索框 | ✅ | full-acceptance #3 |
| E2E-SRV-002 | E2E | **无 CSV 导出按钮** | 按钮不存在 | ✅ | e2e/pages/servers.spec.mjs |
| E2E-SRV-003 | E2E | 分类筛选可见 | chip/文案 | ✅ | e2e/pages/servers-filter.spec.mjs |
| MCP-SRV-001 | MCP | 打开筛选抽屉 | 分类/状态 chip | ✅ | 沉淀 E2E-SRV-003 |

---

## 4. 终端 Terminal `#/terminal`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-TERM-001 | UT | WebSSH payload 校验 | 非法 host 拒绝 | ✅ | test_webssh_terminal_payload.py |
| IT-TERM-001 | IT | WS /ws/webssh 握手 | 401 无 token | ✅ | tests/integration/test_webssh_auth.py |
| E2E-TERM-001 | E2E | 页加载 idle 提示 | 未选服务器文案 | ✅ | e2e/pages/terminal.spec.mjs |
| MCP-TERM-001 | MCP | 选子机不连真 SSH | UI 状态 idle | ✅ | 沉淀 e2e/pages/terminal.spec.mjs |

---

## 5. 文件 Files `#/files`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-FILE-001 | UT | ls 解析 | unix ls 输出 | ✅ | test_unix_ls.py |
| UT-FILE-002 | UT | browse etag | 304 条件 | ✅ | test_files_browse_etag.py |
| UT-FILE-003 | UT | 提权路径策略 | sudo 边界 | ✅ | test_files_elevation.py |
| IT-FILE-001 | IT | GET browse | 列表 JSON | ✅ | tests/integration/test_files_browse.py |
| CH-FILE-001 | CH | 浏览→上传 staging→确认 | staging 清理 | ✅ | tests/chain/test_file_staging_flow.py |
| E2E-FILE-001 | E2E | 文件页表格 | 路径栏可见 | ✅ | e2e/pages/files.spec.mjs |
| MCP-FILE-001 | MCP | 目录树展开一级 | 无 500 toast | ✅ | 沉淀 e2e/pages/files.spec.mjs |

---

## 6. 推送 Push `#/push`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-PUSH-001 | UT | batch_id 生成唯一 | UUID 格式 | ✅ | test_sync_batch_id.py |
| UT-PUSH-002 | UT | rsync 权限位 | 映射正确 | ✅ | test_rsync_push_permissions.py |
| UT-PUSH-003 | UT | upload form 字段 | schema | ✅ | test_sync_upload_form.py |
| IT-PUSH-001 | IT | POST sync 立即 accepted | `accepted:true` 非阻塞 | ✅ | tests/integration/test_push_sync.py |
| IT-PUSH-002 | IT | GET /api/sync/logs | 分页 | ✅ | tests/integration/test_servers_dashboard.py |
| CH-PUSH-001 | CH | accepted→WS progress→log 行 | batch 状态终态 | ✅ | tests/chain/test_push_async_flow.py |
| E2E-PUSH-001 | E2E | 推送页控件 | 目标路径+提交 | ✅ | full-acceptance #6 |
| E2E-PUSH-002 | E2E | 提交后短暂 loading 非一直转圈 | submitting 结束 | ✅ | e2e/pages/push.spec.mjs |
| MCP-PUSH-001 | MCP | 选测试子机 dry-run 提交 | toast + 进度表 | ✅ | e2e/pages/push.spec.mjs（mock 子机+API） |

---

## 7. 脚本 Scripts `#/scripts`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-SCR-001 | UT | dispatch 路由 | server_ids 校验 | ✅ | test_script_dispatch.py |
| IT-SCR-001 | IT | CRUD /api/scripts | 201/200/404 | ✅ | tests/integration/test_scripts_executions.py |
| E2E-SCR-001 | E2E | 脚本列表+新建按钮 | 无 JS 错误 | ✅ | e2e/pages/scripts.spec.mjs |
| MCP-SCR-001 | MCP | Monaco 编辑器加载 | 无 JS 错误 | ✅ | 沉淀 e2e/pages/scripts.spec.mjs |

---

## 8. 执行看板 Executions `#/executions`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-EXEC-001 | UT | 进度分组 | 按 server 聚合 | ✅ | test_exec_detail_grouping.py |
| UT-EXEC-002 | UT | MySQL 持久化字段 | 状态机 | ✅ | test_script_execution_mysql_persist.py |
| IT-EXEC-001 | IT | GET /api/script-executions | 分页+filter | ✅ | tests/integration/test_scripts_executions.py |
| CH-EXEC-001 | CH | 创建执行→轮询→完成 | status=completed | ✅ | tests/chain/test_script_exec_flow.py |
| E2E-EXEC-001 | E2E | 看板页加载 | 表+筛选 | ✅ | e2e/pages/executions.spec.mjs |
| E2E-EXEC-002 | E2E | 展开执行详情行 | stdout/面板 | ✅ | e2e/pages/executions-detail.spec.mjs |
| MCP-EXEC-001 | MCP | 展开执行详情行 | stdout 片段 | ✅ | 沉淀 e2e/pages/executions-detail.spec.mjs |

---

## 9. 凭据 Credentials `#/credentials`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-CRED-001 | UT | poller 周期 | mock clock | ✅ | test_credential_poller.py |
| IT-CRED-001 | IT | 列表不返回明文 secret | 脱敏 | ✅ | tests/integration/test_presets.py |
| E2E-CRED-001 | E2E | 凭据对话框表单校验 | 必填红框 | ✅ | e2e/pages/credentials.spec.mjs |
| MCP-CRED-001 | MCP | 新建凭据表单校验 | 必填红框 | ✅ | 沉淀 e2e/pages/credentials.spec.mjs |

---

## 10. 调度 Schedules `#/schedules`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-SCH-001 | UT | next_run 计算 | cron/周期 | ✅ | test_schedule_next_run.py |
| UT-SCH-002 | UT | cycle 标签 | 仅开始周期 | ✅ | test_schedule_cycle.py |
| UT-SCH-003 | UT | runner once | 不重复触发 | ✅ | test_schedule_runner_once.py |
| IT-SCH-001 | IT | CRUD schedules | 审计 | ✅ | tests/integration/test_schedules.py |
| E2E-SCH-001 | E2E | 新建调度对话框 | ✅ | full-acceptance #9 |
| E2E-SCH-002 | E2E | 新建调度周期中文 | 开始执行周期 | ✅ | e2e/pages/schedules-cycle.spec.mjs |
| MCP-SCH-001 | MCP | 周期字段中文标签 | 与后端一致 | ✅ | 沉淀 E2E-SCH-002 |

---

## 11. 重试 Retries `#/retries`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-RET-001 | UT | runner outcome | 成功/失败计数 | ✅ | test_retry_runner_outcome.py |
| IT-RET-001 | IT | GET /api/retries | 队列列表 | ✅ | tests/integration/test_retries_commands.py |
| E2E-RET-001 | E2E | 重试页无行级重试按钮（空表） | 表+筛选 | ✅ | e2e/pages/retries.spec.mjs |
| MCP-RET-001 | MCP | 手动重试按钮 disabled 态 | 无选中时 | ✅ | 沉淀 e2e/pages/retries.spec.mjs |

---

## 12. 命令日志 Commands `#/commands`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| IT-CMD-001 | IT | 命令/推送/会话三视图 API | 200 | ✅ | tests/integration/test_retries_commands.py |
| E2E-CMD-001 | E2E | 切换三视图 | ✅ | full-acceptance #11 |
| E2E-CMD-002 | E2E | 推送日志时间格式 | 无裸 ISO UTC | ✅ | e2e/pages/commands-time.spec.mjs |
| MCP-CMD-001 | MCP | 推送日志表北京时间 | 列格式 | ✅ | 沉淀 E2E-CMD-002 |

---

## 13. 告警 Alerts `#/alerts`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-ALT-001 | UT | Agent 恢复去重 | 同 metric 1 条 | ✅ | test_agent_recovery_dedup.py |
| UT-ALT-002 | UT | Telegram 时间格式 | 北京时间 | ✅ | test_telegram_time_format.py |
| IT-ALT-001 | IT | GET /api/alerts + WS | 推送事件 | ✅ | tests/integration/test_alerts_audit.py |
| CH-ALT-001 | CH | 心跳告警→恢复→TG 一条 | Redis+mock TG | ✅ | tests/chain/test_alert_recovery_flow.py |
| E2E-ALT-001 | E2E | 告警筛选 UI | ✅ | full-acceptance #12 |
| E2E-ALT-002 | E2E | 告警表时间格式 | 无裸 ISO UTC | ✅ | e2e/pages/alerts-time.spec.mjs |
| MCP-ALT-001 | MCP | 告警表时间列 | 无 UTC 裸字符串 | ✅ | 沉淀 E2E-ALT-002 |

---

## 14. 审计 Audit `#/audit`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| IT-AUD-001 | IT | GET /api/audit 分页 | CUD 记录 | ✅ | tests/integration/test_alerts_audit.py |
| E2E-AUD-001 | E2E | 审计页+分页 | ✅ | full-acceptance #13 |
| E2E-AUD-002 | E2E | 审计表时间格式 | 无裸 ISO UTC | ✅ | e2e/pages/audit-time.spec.mjs |
| MCP-AUD-001 | MCP | 时间列北京时间 | 与设置无关 | ✅ | 沉淀 E2E-AUD-002 |

---

## 15. 设置 Settings `#/settings`

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-SET-001 | UT | Telegram 设置校验 | token 格式 | ✅ | test_settings_telegram.py |
| IT-SET-001 | IT | PATCH settings 禁止覆盖密钥 | 403 | ✅ | tests/integration/test_settings_security.py |
| E2E-SET-001 | E2E | 各 section 可见 | ✅ | e2e/pages/settings.spec.mjs + full-acceptance #14 |
| MCP-SET-001 | MCP | 保存设置 toast | 成功中文 | ✅ | e2e/pages/settings.spec.mjs E2E-SET-002 |

---

## 16. Agent 子机（无 SPA 页）

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-AGT-001 | UT | heartbeat TTL | Redis 过期 | ✅ | test_agent_heartbeat_ttl.py |
| UT-AGT-002 | UT | per-server API key | 隔离 | ✅ | test_agent_per_server_key.py |
| UT-AGT-003 | UT | version 上报 | 语义化版本 | ✅ | test_agent_version.py |
| IT-AGT-001 | IT | POST /api/agent/heartbeat | 200 | ✅ | tests/integration/test_agent_heartbeat.py |
| CH-AGT-001 | CH | 心跳→Redis→仪表盘计数 | 一致 | ✅ | tests/chain/test_agent_dashboard_flow.py |

---

## 17. 批量 / 后台任务

| ID | 层 | 用例 | 期望 | 状态 | 实现 |
|----|-----|------|------|------|------|
| UT-BAT-001 | UT | batch job 持久化 | ✅ | test_server_batch_job_persist.py |
| UT-BAT-002 | UT | reconcile 状态机 | ✅ | test_server_batch_reconcile.py |
| UT-SYNC-001 | UT | sync_log purge | ✅ | test_sync_log_purge.py |
| CH-BAT-001 | CH | 批量操作→job 表→完成 | ✅ | tests/chain/test_batch_job_flow.py |

---

## 覆盖率汇总（2026-06-08 估算）

| 层 | 已有 | 待写 | 合计 |
|----|------|------|------|
| UT | ~45 | ~15 | ~60 |
| IT | ~26 | ~11 | ~37 |
| CH | ~9 | ~5 | ~14 |
| E2E | ~35 | ~2 | ~37 |
| MCP | ~18（沉淀 E2E） | ~3（manual/安装） | ~21 |
| **合计** | **~133** | **~36** | **~169** |

---

## PR 检查清单

- [ ] 改动域在表中至少更新 1 行状态
- [ ] 新 API 必有 `IT-*`；新纯函数必有 `UT-*`
- [ ] UI 行为变更必有 `E2E-*` 或 `MCP-*` 计划
- [ ] 破坏性 E2E 仅对测试子机
