Files
gochat/docs/qa/2026-07-15-cdp-user-function-test-plan.md
T
Rogee dccd4b93f7 fix: 修复第二轮回归发现的已知 BUG
## 修复清单

### 后端路由修复
1.  — 新增 GET 路由与现有 POST 并存
   - 前端 widget.html 原使用 GET 调用但路由仅注册 POST,导致 404
   - 修复: 注册 GET + POST 双路由,handler 支持 query param + JSON body

### 前端修复
2.  — Config 调用方式修正
   - GET /api/v1/widget/config?website_token= → POST /api/v1/widget/config
   - 发送 JSON body: {"website_token":"..."}
   - 后端 Config handler 已同时支持两种调用方式

### 文档更新
3. 修正测试计划 §13.7 已知未实现端点状态
   - 标记 4 项已验证正常(copilot/config、agent_bot_inboxes/、reports/overview)
   - 标记 2 项已修复(widget/config GET+POST)
   - 保留 1 项 P3 未实现(conversation_workflows 需完整 CRUD handler)

### 已验证非 BUG 项
- 仪表盘会话计数的0问题:实际显示 1/13/14 ✅(之前为 stale snapshot)
- 会话列表为空:filter status=open 过滤掉 snoozed 会话 ✅(设计如此)
- Reports API:正确路由 /reports?metric=&since=&until= ✅
2026-07-28 21:46:53 +08:00

1052 lines
56 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CDP 用户功能全量测试计划
> 创建:2026-07-15
> 最后更新:2026-07-22
> 目标:连接已启动的 GoChat 本地服务,用 Chrome DevTools Protocol 按真实用户路径覆盖 dashboard / widget / settings / Captain / Copilot / public surfaces。
> 当前服务由人工启动,不由测试脚本托管。
## 0. 当前前提
已启动:
```bash
pnpm dev:backend
pnpm dev:frontend
GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_01 FAKE_AUTO_REPLY=true pnpm fake:start
```
本轮计划默认以上述三条人工启动命令为权威运行基线;后续 CDP 验收、数据补齐清单、以及 `fake:ai` 设计都以这套本地端口和进程拓扑为前提。
### 0.1 当前推荐的 CDP 连接方式(Sunday, July 19, 2026)
这轮计划默认优先复用已经打开的 Chrome,会比重新拉起 headed 浏览器更稳,也更符合“接现有人工会话继续点测”的目标。
建议默认使用:
| 项 | 值 | 说明 |
|---|---|---|
| CDP debug port | `127.0.0.1:9222` | 优先 attach 已存在的 Chrome 会话 |
| version probe | `http://127.0.0.1:9222/json/version` | 读取 `webSocketDebuggerUrl` |
| 当前可复用 CLI | `/home/rogee/.npm/_npx/15c61037b1978c83/node_modules/chrome-devtools-mcp/build/src/bin/chrome-devtools.js` | 已在当前 QA 流程中实际使用过 |
建议连接顺序:
1. 先请求 `http://127.0.0.1:9222/json/version`,确认能拿到 `webSocketDebuggerUrl`。
2. 成功后直接 attach,不重新登录、不重开新浏览器。
3. 进入测试前先做一次 snapshot,确认当前 tab、当前账号、当前 account id。
4. 若 `9222` 不可用,再退回新开 Chrome,并固定 `--remote-debugging-port=9222`,避免同一轮报告里混入多套浏览器状态。
连接成功标准:
- `json/version` 返回 200;
- 能对当前 tab 成功 snapshot;
- 能读取 console / network;
- 能通过真实点击让页面发生路由变化。
### 0.2 测试环境异常时的重置策略(Wednesday, July 22, 2026)
这轮点测里已经确认过:测试环境一旦进入“持续重连 / `/cable` 抖动 / 页面大量 `429` / fake 平台残留旧消息”的脏状态,继续硬点只会把环境噪音和真实缺陷混在一起。因此后续执行时,把“允许重置并继续”写成正式策略,而不是临场救火。
重置优先级:
1. 先重置 fake 平台内存态,不动前后端。
2. 若 dashboard 已出现连续 `429`、`正在重连...` 挡点击、或 `/cable` 无法恢复,再重启 backend。
3. frontend 只在 Vite 自身白屏、热更新异常、或静态资源 5xx 时才重启。
4. 浏览器 tab 状态明显污染时,可以保留现有 Chrome/CDP 会话,但需要重新从 `/app/login` 走一遍点击链路。
推荐重置动作:
```bash
curl -fsS -X POST http://127.0.0.1:9100/api/reset
curl -fsS http://127.0.0.1:3000/health
curl -fsS http://127.0.0.1:9100/health
```
若 backend 已进入脏状态,直接重新执行:
```bash
pnpm dev:backend
```
若 fake 进程已退出或 webhook 指向失效,重新执行:
```bash
GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_01 FAKE_AUTO_REPLY=true pnpm fake:start
```
重置后的恢复标准:
- `GET /health` 返回 200。
- `GET http://127.0.0.1:9100/health` 返回 `{"status":"ok"}`。
- 登录后 `/cable` 不再持续 401/403/断开重连。
- dashboard 底部不再常驻 `正在重连...` 浮层。
- 同一页面不再连续出现大批量 `429`。
报告要求:
- 一旦发生重置,必须在 QA report 中记下“重置原因 / 重置动作 / 重置后恢复结果”。
- 重置后恢复通过的页面,要和“真实功能缺陷”分开归类,避免把环境污染误记成产品 bug。
测试入口:
| 服务 | URL | 用途 |
|---|---|---|
| backend | `http://127.0.0.1:3000` | API、webhook、WebSocket |
| frontend | `http://127.0.0.1:3036` | dashboard / widget 前端 |
| fake channel | `http://127.0.0.1:9100` | 外部客户消息、出站消息断言 |
| CDP | `http://127.0.0.1:<debug-port>` | 连接现有 Chrome,优先不新开 headed browser |
## 1. 测试目标
1. 用 CDP 模拟真实用户点击、输入、上传、保存、导航、退出登录。
2. 每个页面至少验证:可打开、核心数据加载、主要操作可执行、错误态可见、无异常 API/console、刷新后状态仍正确。
3. 消息链路必须验证:客户入站 → dashboard 实时出现 → 客服回复 → fake 收到出站 → auto reply 回流 → dashboard 无刷新更新。
4. Chatwoot parity 相关页面以“前端实际请求成功 + UI 可用”为准,不只看路由存在。
5. AI/Copilot/Captain 测试不依赖真实 LLM Key;先补 `fake:ai`,让页面功能和后端调用链可自动断言。
6. 除登录页、widget/public 入口页外,dashboard 内部页面一律通过真实 UI 点击进入,不直接 `open` 深层内部 URL,避免把路由可达误判成用户可达。
## 2. CDP 执行协议
每个页面统一记录:
- `page.url`
- `document.title`
- `#app` 是否挂载
- console `error` / `warning`
- `Network.responseReceived` 中所有 `/api`、`/platform`、`/public`、`/cable`、`/webhooks` 的状态码
- `Runtime.exceptionThrown`
- 关键 DOM 文案或按钮存在性
- 操作前后截图
- 操作产生的 API 请求和响应摘要
导航约束:
- 允许直接打开:`/app/login`、widget/public 根入口、必要的外部 fake/fake:ai 观察接口。
- 不允许直接打开:`/app/accounts/:id/...` 下的深层功能页作为“通过”依据。
- dashboard 内导航必须由登录后侧边栏、列表项、按钮、tab、面包屑、弹窗入口逐步点击完成。
- 若页面只能通过手输 URL 才能访问,记录为信息架构或入口缺失问题,而不是直接算页面通过。
失败分级:
| 等级 | 标准 |
|---|---|
| P0 | 登录失败、dashboard 不可用、消息收发断、权限泄露、数据保存丢失 |
| P1 | 页面主要 CRUD 不可用、关键 API 4xx/5xx、实时事件错误 |
| P2 | 局部功能不可用、空态错误、表单校验不清晰 |
| P3 | 文案、布局、轻微 console warning、非阻断体验问题 |
最小 CDP harness 只需要:
1. 连接 `http://127.0.0.1:<debug-port>/json/version` 拿 `webSocketDebuggerUrl`。
2. `Page.enable`、`Runtime.enable`、`Network.enable`。
3. 注入 `window.__gochatQa` 记录 console、fetch、XHR、resource timing。
4. 用 `Runtime.evaluate` 点击和输入;必要时用 `Input.dispatchKeyEvent`。
5. 每个页面结束调用 `assertNoFailedBackendRequests()`。
跳过:新测试框架、复杂 Page Object、视觉 diff。等第一轮人工可读报告稳定后再加。
## 3. 预检查
| 检查 | 命令/动作 | 通过标准 |
|---|---|---|
| backend health | `GET /health` | 200 |
| frontend mount | 打开 `http://127.0.0.1:3036/app/login` | login 页面可见 |
| fake health | `GET http://127.0.0.1:9100/health` | `status=ok` |
| CDP tooling | `command -v npx && node --version && npm --version` | `npx` 可用,Node/npm 正常 |
| fake 配置 | `POST /api/config` | webhook 指向 `fake_01`,auto reply 开启 |
| 登录账号 | seed 或 DB 查询 | 管理员、客服、普通 agent 可登录 |
| WebSocket | 登录后监听 `/cable` | 不循环 401/403/重连 |
| route parity 基线 | 参考 `docs/parity/route-parity.md` | 页面请求不应出现缺失路由 |
## 4. 基础测试数据缺口
需要补齐这些数据,否则“全量实际功能测试”会退化成空态浏览:
| 数据 | 最低数量 | 用途 |
|---|---:|---|
| Account | 1 | 主测试租户 |
| Administrator | 1 | 设置、成员、平台配置 |
| Agent | 2 | 分配、团队、在线状态、跨坐席实时 |
| Custom role 用户 | 1 | 权限边界 |
| Fake inbox `fake_01` | 1 | 消息 E2E |
| Website inbox | 1 | widget、pre-chat、campaign |
| API inbox | 1 | inbox 类型覆盖 |
| Email inbox | 1 | 邮件配置、SMTP/IMAP 页面 |
| Voice/Twilio inbox | 1 | voice 设置页和降级态 |
| Contact | 8+ | 列表、搜索、合并、标签、公司 |
| Company | 3+ | 公司详情、联系人关联 |
| Conversation | 12+ | open/resolved/pending/snoozed、assignee、team、label、priority |
| Messages | 每会话 3+ | incoming/outgoing/private/note/attachment/email |
| Labels | 5 | 会话/联系人标签、报表 |
| Teams | 2 | 团队分配、团队报表 |
| Canned responses | 3 | 回复框插入、CRUD |
| Macros | 3 | 宏执行、条件动作 |
| Automation rules | 3 | create/edit/clone/delete、条件校验 |
| Custom attributes | contact/conversation/company 各 2 | 表单渲染、筛选 |
| SLA policies / applied SLA | 2 | SLA 报表 |
| CSAT responses | 5 | CSAT 报表、公开页 |
| Dashboard apps | 1 | 侧边栏 iframe/app surface |
| Webhook subscriptions | 2 | 集成 webhook CRUD |
| Help center portal | 1 | portal、locale、category、article |
| Campaigns | live chat / sms / whatsapp 各 1 | campaign 页面 |
| Notifications | 5 | 通知列表、已读 |
| Audit logs | 5 | audit 页面 |
| Agent capacity policies | 2 | assignment policy |
| Captain assistant | 1 | Captain 页面根对象 |
| Captain document | 3 | 文档列表、上传/同步状态 |
| Captain response/FAQ | 5 | responses、pending |
| Captain scenario | 2 | scenario 页面 |
| Captain custom tool | 1 | tools 页面 |
| Copilot config | 1 fake provider | AI 功能不打真实外网 |
### 4.1 首轮必须先补的数据(否则会大面积 blocked)
| 优先级 | 数据 | 最低要求 | 影响范围 |
|---|---|---|---|
| P0 | Administrator | 1 个可登录管理员 | 所有 settings / Captain / reports |
| P0 | Agent | 2 个可登录 agent | 分配、协作、在线状态、mentions |
| P0 | Fake inbox `fake_01` | 已绑定 webhook 且可收发 | 会话主链路、实时消息 |
| P0 | Website inbox | 1 个带 `website_token` 的 live chat inbox | widget、pre-chat、campaign |
| P0 | 基础 conversations/messages | 至少 6 个会话、每个 3 条消息 | dashboard 列表、详情、筛选、报表 |
| P1 | Labels / Teams | labels 5 个、teams 2 个 | 标签、团队过滤、自动化、报表 |
| P1 | Contacts / Companies | contacts 8+、companies 3+ | CRM、搜索、合并、关联 |
| P1 | Custom attributes | 三类对象各 2 个 | 筛选器、详情表单、自动化 |
| P1 | Canned responses / Macros | 各 3 条 | 回复提效、设置页 CRUD |
| P1 | Automation rules | 3 条可编辑规则 | 自动化列表、编辑、校验 |
| P1 | Copilot fake provider | 1 套假配置 | Copilot/Captain 页面进入与联调 |
| P2 | Help center portal | 1 portal + category + article | portal/public/help center |
| P2 | Campaigns | live chat 至少 1 条 | campaign 页面、widget 触发 |
| P2 | CSAT / SLA 数据 | CSAT 5 条、SLA 2 条 | 报表、公开页、inbox csat |
| P2 | Audit logs / notifications | 各 5 条 | 列表页、跳转、筛选 |
建议策略:
1. 能由 UI 自举创建的,优先在首轮 CDP 中顺手创建并复用。
2. 会阻断主链路的种子数据(管理员、agent、fake/website inbox、基础 conversations)应在执行前一次性准备好。
3. Captain/Copilot 相关不要等真实第三方 Key,直接用 `fake:ai` 打通请求与错误态。
### 4.2 数据准备方式建议
把“缺数据”再分成三类,执行时更省时间:
| 类别 | 数据 | 建议来源 | 是否需要执行前准备 | 备注 |
|---|---|---|---|---|
| A | Administrator / Agent / Custom role 用户 | seed + 后台 Settings 手工补齐 | 是 | 登录、权限、分配依赖它们 |
| A | Fake inbox `fake_01` | 当前 fake channel + inbox 配置 | 是 | 主消息链路阻断项 |
| A | Website inbox | Settings 新建或 seed | 是 | widget / campaign / pre-chat 依赖 |
| A | 基础 conversations / messages | fake channel 批量造数 | 是 | 推荐至少覆盖 open / pending / resolved / snoozed |
| B | Contacts / Companies / Labels / Teams | 优先走 UI 创建,缺口再补 seed | 否 | 同时可顺手验证 CRUD |
| B | Canned responses / Macros / Automation rules | 走 UI 创建 | 否 | 适合首轮 CDP 过程中创建并复用 |
| B | Custom attributes | 走 UI 创建 | 否 | 可直接覆盖表单和筛选能力 |
| B | Help center portal / locale / article | 走 UI 创建 | 否 | 既补数据又验证 portal 后台 |
| C | CSAT / SLA / Audit logs / Notifications | 定向 seed 或接口回灌 | 视页面而定 | 纯空态也能先验渲染,但无法完成“全量功能”断言 |
| C | Captain documents / responses / scenarios / tools | `fake:ai` + Captain 后台创建 | 是(若要做 AI 主链路) | 没有 `fake:ai` 时只能做页面渲染检查 |
| C | Campaigns(live chat / sms / whatsapp) | Settings / Campaign UI 创建 | 否 | 需要 website inbox 与 portal 先到位 |
建议最小准备顺序:
1. 先准备管理员、2 个 agent、`fake_01`、website inbox。
2. 用 fake channel 批量灌入基础会话和消息。
3. 再通过 UI 顺手创建 labels / teams / macros / canned responses / custom attributes。
4. AI 相关最后统一切到 `fake:ai`,避免前面主链路被外部依赖拖住。
### 4.3 当前仓库已具备的数据准备能力 vs 仍需补齐项
为了避免把“已有 smoke seed 能力”和“真正缺失的数据/能力”混为一谈,这里按当前仓库实际情况再拆一次。
当前仓库里已经存在可直接复用的 smoke seed 基线,入口是:
```bash
cd backend
go run ./cmd/gochat seed
```
按当前 `cmd/gochat seed` 的实现,已经能稳定准备出这些基础对象:
| 类别 | 当前 seed 覆盖情况 | 备注 |
|---|---|---|
| Administrator | 已覆盖 1 个 | 默认 `admin@gochat.local / changeme`,可通过环境变量覆盖 |
| Account | 已覆盖 1 个 | 默认 `Test Account` |
| Website inbox | 已覆盖 1 个 | `web_widget` inbox,带 `website_token` |
| Voice/Twilio-like inbox | 已覆盖 1 个 | 当前是 `twilio_sms` 型 smoke inbox,适合页面和降级态验证 |
| Contact | 已覆盖 1 个 | `Smoke Customer` |
| Company | 已覆盖 1 个 | `Smoke Company` |
| Conversation | 已覆盖 1 个 | 已绑定 contact / inbox / assignee |
| Messages | 已覆盖 3 条 | incoming / outgoing / CSAT template 各 1 条 |
| Help Center portal/category/article | 已覆盖 1 套 | 适合 articles / preview / public help center 基线 |
| CSAT | 已覆盖 1 条模板消息 | 够做基础渲染,不够做分布/列表型报表 |
| SLA policy | 已覆盖 1 条 | 够做基础页面进入 |
| Custom role | 已覆盖 1 条 | 可做权限页基线 |
| Capacity policy | 已覆盖 1 条 | 可做 assignment policy 基线 |
| Captain assistant | 已覆盖 1 条 | 只够列表/详情基线,不够真实 AI 回路 |
| Captain message | 已覆盖 1 条 | 适合 conversation 内 Captain message 渲染 |
| Agent bot | 已覆盖 1 条 | 适合 agent bot 页面基线 |
基于当前代码和脚本,仍然明确缺失、会影响“全量实际功能测试”的项如下:
| 优先级 | 缺失项 | 为什么缺 | 直接影响 |
|---|---|---|---|
| P0 | 第二个可登录 agent | 当前 smoke seed 只准备管理员,没有双坐席协作基线 | 分配、在线状态、mentions、团队协作、跨坐席实时 |
| P0 | `fake_01` 对应 inbox 基线 | 当前 seed 侧重 `web_widget` smoke inbox,不是 fake channel inbox | fake webhook 主链路、会话实时回流、回复 E2E |
| P0 | 6+ 条不同状态会话 | 当前 seed 只有 1 条 open conversation | dashboard 列表、筛选、报表、批量操作、状态流转 |
| P0 | 更丰富的消息类型 | 当前仅 text + CSAT template | private note、attachment、email、AI 消息、系统事件渲染 |
| P1 | Teams 2 条以上 | 当前未见 smoke team 基线 | team 过滤、team assign、team report |
| P1 | Labels 5 条以上 | 当前会话只有字符串标签,不是完整标签数据集 | 标签设置页、筛选器、报表 |
| P1 | Contacts 8+ / Companies 3+ | 当前各只有 1 条 | CRM 列表、搜索、合并、关联、空态外真实分页 |
| P1 | Canned responses / Macros / Automations | 当前 seed 未覆盖 | 设置 CRUD、回复提效、自动化规则 |
| P1 | Custom attributes 三类对象各 2 条 | 当前仅对象上有少量属性值,不是完整属性定义 | 表单、筛选、详情编辑 |
| P1 | Notifications / Audit logs 有内容 | 当前 seed 未覆盖可见事件流 | 通知页、审计日志页、跳转链路 |
| P1 | Copilot fake provider | 当前仓库只有真实配置入口,没有本地 fake provider 基线 | Copilot/Captain 成功/失败/超时/429 验证 |
| P2 | Email inbox / API inbox | 当前 smoke seed 未覆盖 | channel create/edit、provider 配置、空态/降级态之外的实际流程 |
| P2 | Campaign 样本 | 当前未覆盖 live chat / sms / whatsapp 实例 | campaign 列表、编辑、启停、触发条件 |
| P2 | Captain documents / responses / scenarios / tools | 当前仅 assistant/message 基线 | Captain 子页 CRUD、embedding、playground、tool 调用 |
| P2 | 多 locale Help Center 数据 | 当前 seed 基本是单 portal、单 locale | locales / categories / settings 多语言验证 |
建议把数据准备再拆成三条线并行推进:
1. `cmd/gochat seed` 继续承担“可登录 + 可进入页面”的 smoke 基线。
2. `channels/fake` 负责批量制造真实消息、会话状态变化、typing、agent online/offline。
3. 新增 `fake:ai` 后,再把 Copilot / Captain 的成功、失败、超时、429 路径补齐成可回放证据。
### 4.4 按页面分组看数据阻断关系
为了执行时不把“页面 bug”和“数据没准备好”混为一谈,建议按页面分组提前标记它们依赖的最小数据集:
| 页面组 | 最小依赖数据 | 缺失时会怎么 blocked |
|---|---|---|
| 登录 / 账号切换 / Profile | administrator、至少 1 个 account、至少 1 个可登录 agent | 无法进入主应用、无法验证 account switch / profile 保存 |
| Dashboard / Conversations / Mentions / Unattended | `fake_01` inbox、6+ conversations、3+ message types、2 个 agent | 列表空、实时链路无证据、分配/协作不可测 |
| Contacts / Companies | 8+ contacts、3+ companies、custom attributes | 只能看到空态,搜索/合并/关联无法完成 |
| Reports | conversations、labels、teams、CSAT、SLA、audit/event 数据 | 图表和表格只剩空态,无法验证筛选/导出/维度切换 |
| Settings - Agents / Teams / Labels | 2 个 agent、2 个 teams、5 个 labels、1 个 custom role | 只能验渲染,无法验成员绑定、权限边界、筛选联动 |
| Settings - Inboxes / Channel create | website inbox、fake inbox、api/email/voice 至少部分样本 | 只能看入口,无法验证编辑页、collaborators、business hours、channel 特有字段 |
| Settings - Automation / Macros / Canned Responses | 3 automation、3 macros、3 canned responses | 列表可打开但无法验证 CRUD 与执行结果 |
| Help Center | 1 portal、1 locale、2 categories、3 articles | 只能做最浅页面进入,发布/预览/分类/多语言不完整 |
| Campaigns | website inbox、portal、至少 1 live chat campaign 样本 | 只能看空态,无法验证创建/启停/触发条件 |
| Notifications / Audit logs | 5+ notifications、5+ audit logs | 只能确认页面壳存在,跳转和已读不可测 |
| Copilot / Captain | fake provider、assistant、documents、responses、scenarios、tools | 只能验页面渲染,无法做 AI 成功/失败/超时全链路 |
| Widget / public | website inbox、public help center、CSAT 样本、真实站内入口 | 即使内部后台健康,也无法完成 public/user 侧闭环验收 |
推荐执行时先给每个页面组打一个前置标签:
- `ready`:数据和入口都齐,可以做完整功能断言
- `render-only`:只能做渲染/空态/入口断言
- `blocked-by-data`:缺数据,先补数据再测
- `blocked-by-entrypoint`:入口未接通,不应靠手输内部 URL 绕过
## 5. `fake:ai` 最小方案
先加一个本地 fake AI 服务,目标是“可测”,不是模拟完整 LLM。
建议命令:
```bash
pnpm fake:ai
```
建议端口:`9200`。
最小 API:
| 方法 | 路径 | 用途 | 响应 |
|---|---|---|---|
| GET | `/health` | 健康检查 | `{ "status": "ok" }` |
| GET | `/v1/models` | OpenAI-compatible 模型列表 | `fake-chat`, `fake-embedding` |
| POST | `/v1/chat/completions` | Copilot / Captain 回复 | 回显 prompt 摘要,支持固定 delay/error |
| POST | `/v1/embeddings` | 文档 embedding | 固定维度向量 |
| POST | `/api/config` | 切换模式 | `ok/error/slow/rate_limit` |
| GET | `/api/requests` | 测试断言 | 最近请求列表,脱敏 Authorization |
| POST | `/api/reset` | 清空状态 | `{ "status": "ok" }` |
Provider 配置建议:
| 字段 | 值 |
|---|---|
| provider | `openai_compatible` |
| base_url | `http://127.0.0.1:9200/v1` |
| api_key | `fake-ai-key` |
| chat model | `fake-chat` |
| embedding model | `fake-embedding` |
| embedding dimensions | `1536` |
必须覆盖的 AI 场景:
1. Copilot 配置页保存 fake provider。
2. 测试连接成功、失败、超时、429 四种状态。
3. Captain Playground 输入问题,收到 fake answer。
4. Captain document embedding 调用 fake embeddings。
5. Agent 回复框 AI 改写/建议调用 fake chat。
6. 错误态不泄露 API key。
跳过:真实 OpenAI/Anthropic/DeepSeek 兼容性;等 fake 链路稳定再做外部 provider smoke。
### 5.1 `fake:ai` 实现方式建议(直接复用 fake channel 骨架)
为了少造轮子,`fake:ai` 建议直接按 `channels/fake` 的组织方式复制一套最小骨架:
| 项 | 建议 |
|---|---|
| 目录 | `channels/fake-ai/` |
| 启动脚本 | 根 `package.json` 增加 `fake:ai` / `fake:ai:dev` / `fake:ai:test` |
| 运行时 | `tsx src/index.ts` |
| HTTP 框架 | 继续用 Express,和 fake channel 保持一致 |
| 状态存储 | 先用内存 store,记录最近请求、模式、延迟、错误注入 |
| 模式切换 | `/api/config` 支持 `ok` / `error` / `slow` / `rate_limit` |
| 测试断言 | `/api/requests` 返回最近 chat / embeddings 请求摘要 |
| 脱敏 | 所有请求日志都隐藏 Authorization / api_key |
建议脚本:
```json
{
"fake:ai": "cd channels/fake-ai && tsx src/index.ts",
"fake:ai:dev": "cd channels/fake-ai && tsx watch src/index.ts",
"fake:ai:test": "pnpm --dir channels/fake-ai test"
}
```
这样后续维护会和 `fake:start` 基本同构,排查成本也低。
补充说明:当前仓库根 `package.json` 里已经有 `fake:start / fake:dev / fake:test`,并且 `pnpm-workspace.yaml` 已纳入 `channels/fake`;但还没有现成的 `channels/fake-ai` 目录和 `fake:ai` 脚本,所以这部分目前仍属于待新增测试支撑能力,而不是现成可执行项。
### 5.2 `fake:ai` 断言矩阵
`fake:ai` 不只是“让页面不报错”,还要能作为 CDP 回放时的稳定证据源。建议第一版就固定支持下面这几类断言:
| 场景 | CDP 页面动作 | `fake:ai` 需要返回 | 测试证据 |
|---|---|---|---|
| Provider 连通性测试 | Settings → Copilot → Test connection | 200 + 固定模型列表 | 页面成功提示 + `/api/requests` 有 `GET /v1/models` |
| Provider 保存后首次使用 | 保存 fake provider 后进入 Copilot/Captain | `GET /v1/models` 或首次 chat 请求成功 | 配置持久化成功 + 后续 AI 页面可继续使用 |
| Copilot 发问 | 会话侧边栏输入问题并发送 | `POST /v1/chat/completions` 返回固定答复 | 气泡渲染 + `/api/requests` 记录 prompt 摘要 |
| Captain Playground | Playground 输入问题 | `POST /v1/chat/completions` 返回固定答复 | 页面回复 + 请求日志 |
| Reply suggestion / rewrite / summarize | 回复框触发 AI 建议 | `POST /v1/chat/completions` 返回不同 action 标记 | 建议文本正确落入 UI,对应 action 可区分 |
| Document embedding | 上传 URL/PDF 文档后触发 embedding | `POST /v1/embeddings` 返回固定维度向量 | 文档状态变化 + `/api/requests` 有 embedding 记录 |
| Slow provider | 切到 `slow` 模式后重试 | 延迟 3~8 秒再返回 200 | 页面 loading、可取消/可恢复、无死锁 |
| Error provider | 切到 `error` 模式后重试 | 500 或结构化错误 | toast / inline error 正确展示,不泄露 key |
| Rate limit | 切到 `rate_limit` 模式 | 429 | 页面可见限流反馈、不会假成功 |
| 敏感信息脱敏 | 任意 AI 请求 | `fake:ai` 仅记录掩码后的鉴权信息 | `/api/requests` 不出现明文 `Authorization` / `api_key` |
建议 `fake:ai` 每条请求至少记录这些字段,便于后续 report 复用:
- `ts`
- `method`
- `path`
- `mode`
- `model`
- `account_id`(若请求链路可带出)
- `request_summary`(截断后的 prompt/输入摘要)
- `response_status`
- `latency_ms`
- `auth_masked`
### 5.3 当前仓库核对结果(2026-07-19)
这部分是为了把“计划建议”和“当前仓库现实”分开:
| 项 | 当前状态 | 结论 |
|---|---|---|
| 根脚本 `fake:start / fake:dev / fake:test` | 已存在 | fake channel 现成可用,可直接作为消息 E2E 基线 |
| 根脚本 `fake:ai` | 不存在 | 需要新增 |
| workspace 包 | 当前只纳入 `frontend`、`channels/fake` | `channels/fake-ai` 需要加入 workspace |
| `channels/fake-ai/` 目录 | 不存在 | 需要新建最小服务骨架 |
| 现有 `channels/fake` 结构 | 已具备独立 package + tsconfig | 适合直接镜像出 `fake-ai` 的最小实现 |
因此,“AI 相关测试可参考 fakechannel 搞一个 fake:ai 来支持对接”在当前仓库里应拆成明确的补齐任务:
1. 新建 `channels/fake-ai/`,提供独立 `package.json`、`tsconfig.json`、`src/index.ts`。
2. 根 `package.json` 增加 `fake:ai`、`fake:ai:dev`、`fake:ai:test`。
3. `pnpm-workspace.yaml` 纳入 `channels/fake-ai`。
4. 提供最小 `/health`、`/v1/models`、`/v1/chat/completions`、`/v1/embeddings`、`/api/config`、`/api/requests`、`/api/reset`。
5. GoChat 内新增一套本地 fake provider 配置模板,方便 Copilot / Captain 直接切过去联调。
### 5.4 当前 `channels/fake` 已可直接复用的测试能力(2026-07-19 核对)
为了避免把 `fake channel` 和未来的 `fake:ai` 混在一起,这里把当前已经现成可用的 fake 消息平台能力单独列出来:
| 能力 | 当前端点 | 可直接支撑的测试 |
|---|---|---|
| 健康检查 | `GET /health` | 执行前确认 fake 服务在线 |
| 运行时改配置 | `POST /api/config` | 动态切换 webhook、token、auto reply、delay |
| 客户入站消息 | `POST /api/send` | 造新会话、造入站消息、验证 dashboard 实时出现 |
| 客户回复消息 | `POST /api/reply` | 验证 reply_to / 同会话追加消息 |
| 会话结束事件 | `POST /api/close` | 验证 session.end、关闭链路、状态流转 |
| typing 事件 | `POST /api/typing` | 验证 typing.start / typing.stop UI 提示 |
| agent 在线状态观测 | `POST /api/agent/online`、`POST /api/agent/offline` | 验证 presence / availability 展示 |
| 出站消息留痕 | `GET /api/messages`、`GET /api/messages/:id` | 断言客服回复有没有真正发回 fake 平台 |
| 平台状态观测 | `GET /api/status` | 观察 auto reply、消息计数、agent 状态 |
| 内存态重置 | `POST /api/reset` | 每轮 CDP 测试前清理平台状态 |
基于当前实现,`channels/fake` 已经足够支撑这几类真实验收:
1. fake 客户发消息 → GoChat 创建或追加会话。
2. 客服在 dashboard 回复 → fake 平台能收到 outbound。
3. auto reply 回流 → dashboard 无刷新出现下一条 incoming。
4. typing / session.end / agent online/offline 等外围实时事件可单独回放。
但它还不是“批量数据工厂”,当前仍缺这些会明显影响全量页面验收的能力:
- 批量制造多状态会话(open / pending / resolved / snoozed);
- 批量制造更多消息类型(private note、attachment、email-like、system event);
- 一次性造多联系人、多公司、多标签、多 team 的数据集;
- AI provider 兼容接口(这部分应由独立的 `fake:ai` 负责,而不是继续堆进 `channels/fake`)。
## 6. 页面功能矩阵
### 6.1 Auth / account lifecycle
| 页面 | 路径 | 功能点 |
|---|---|---|
| 登录 | `/app/login` | 正确登录、错误密码、空表单校验、无注册链接、回车提交 |
| SSO 登录 | `/app/login/sso` | 无配置时错误态,配置后跳转态 |
| 重置密码 | `/app/auth/reset/password` | 表单校验、提交反馈 |
| 邮箱确认 | `/app/auth/confirmation` | token 缺失错误态 |
| 无账号 | `/app/no-accounts` | 空账号用户展示 |
| onboarding | `/app/accounts/:id/onboarding` | 首次账号信息表单 |
| suspended | `/app/accounts/:id/suspended` | 账号暂停页 |
### 6.2 Inbox / conversation
| 页面 | 路径 | 功能点 |
|---|---|---|
| Dashboard | `/app/accounts/:id/dashboard` | 会话列表、筛选、排序、在线状态、未读数 |
| 会话详情 | `/conversations/:conversation_id` | 消息渲染、发送、私密备注、附件、emoji、引用、草稿 |
| Inbox 会话 | `/inbox/:inbox_id` | inbox 筛选、列表一致性 |
| Label 会话 | `/label/:label` | 标签过滤、标签增删 |
| Team 会话 | `/team/:teamId` | 团队过滤、团队分配 |
| Custom view | `/custom_view/:id` | 自定义视图过滤、缺失视图重定向 |
| Mentions | `/mentions/conversations` | @ 提及列表 |
| Unattended | `/unattended/conversations` | 未处理会话 |
| Conversation search | 搜索入口 | 搜索结果、跳转详情 |
| Inbox view | `/inbox-view/:type/:id` | 聚合视图、详情页 |
核心操作:
1. fake 客户发消息,dashboard 不刷新出现新会话。
2. 客服回复,fake `/api/messages` 能查到 outbound。
3. auto reply 回流后同一会话追加 incoming。
4. 切换 open/resolved/pending/snoozed。
5. 分配 agent/team,刷新后仍保持。
6. 添加/移除 label、priority。
7. 上传图片/文件,检查预览和下载。
8. private note 不发送到 fake。
9. typing.start/typing.stop 有 UI 指示。
10. 多标签页实时同步。
说明:
- 当前 dashboard 登录后的默认会话请求是 `assignee_type=me`,即“我的”视图。
- fake 入站若创建的是未分配会话,验证会话可见性时应继续点击切到 `未分配的` 或 `所有的`,不要把“我的”视图下不可见误判成实时失败。
### 6.3 CRM
| 页面 | 路径 | 功能点 |
|---|---|---|
| Contacts | `/contacts` | 列表、搜索、分段、标签过滤、新建 |
| Contact detail | `/contacts/:contactId` | 编辑资料、custom attributes、会话历史、备注 |
| Companies | `/companies` | 列表、搜索、新建 |
| Company detail | `/companies/:companyId` | 编辑、关联联系人、历史记录 |
### 6.4 Reports
| 页面 | 路径 | 功能点 |
|---|---|---|
| Overview | `/reports/overview` | 指标卡、日期范围、图表 |
| Conversations | `/reports/conversations` | 表格、导出、筛选 |
| Agents | `/reports/agents` | agent 维度指标 |
| Labels | `/reports/labels` | label 维度指标 |
| Inboxes | `/reports/inboxes` | inbox 维度指标 |
| Teams | `/reports/teams` | team 维度指标 |
| CSAT | `/reports/csat` | 评分分布、评价列表 |
| SLA | `/reports/sla` | SLA 命中/违约 |
| Bot | `/reports/bot` | bot/assistant 指标 |
| Live reports | `/reports/live` | 实时数据刷新 |
### 6.5 Settings
| 页面 | 路径 | 功能点 |
|---|---|---|
| Account | `/settings/account` | 名称、语言、auto-resolve、删除保护 |
| Agents | `/settings/agents/list` | 创建 agent、临时密码、编辑、禁用、重置密码 |
| Teams | `/settings/teams/list` | 创建、成员、编辑、删除 |
| Inboxes list | `/settings/inboxes/list` | channel 列表、创建入口、fake/website 可见 |
| Inbox configuration | `/settings/inboxes/:id` | 名称、欢迎语、允许域名、sender name、保存 |
| Inbox collaborators | `/settings/inboxes/:id/collaborators` | agent 绑定 |
| Pre-chat form | `/settings/inboxes/:id/pre-chat-form` | 字段开关、必填、保存 |
| CSAT inbox | `/settings/inboxes/:id/csat` | 开关、消息模板 |
| Business hours | inbox 子页 | 每周时间、时区 |
| Channel create pages | `/settings/inboxes/new/*` | website/api/email/fake/twilio/line/tiktok/facebook 等表单和错误态 |
| Labels | `/settings/labels/list` | CRUD、颜色 |
| Custom attributes | `/settings/custom-attributes/list` | contact/conversation/company 属性 CRUD |
| Automation | `/settings/automation/list` | CRUD、条件/动作校验 |
| Macros | `/settings/macros` | CRUD、执行宏 |
| Canned responses | `/settings/canned-response/list` | CRUD、插入回复框 |
| Agent bots | `/settings/agent-bots` | bot 列表、绑定 inbox |
| Integrations | `/settings/integrations` | Slack/Linear/Notion/Webhook/Dashboard app 卡片和配置 |
| Webhooks | integration 子页 | CRUD、事件选择、签名字段 |
| Conversation workflow | `/settings/conversation-workflows` | 开关、保存 |
| Assignment policy | `/settings/assignment-policy/*` | assignment、capacity 创建/编辑 |
| Custom roles | `/settings/custom-roles/list` | 权限勾选、角色用户 |
| Audit logs | `/settings/audit-logs/list` | 列表、筛选 |
| Security/SAML | `/settings/security` | 无配置错误态、字段校验 |
| Billing | `/settings/billing` | subscription/limits 降级态 |
| Profile | `/profile/settings` | 资料、密码、消息签名、通知偏好、access token、MFA |
| Notifications | `/notifications` | 列表、已读、跳转 |
| Copilot 配置 | `/settings/copilot` 或当前实际入口 | fake provider 保存、测试连接、账户功能开关 |
### 6.6 Help Center
| 页面 | 路径 | 功能点 |
|---|---|---|
| Portals | `/portals/:navigationPath` | portal 列表、新建 |
| Articles | `/portals/:slug/:locale/articles` | 列表、草稿/发布 tab |
| Article editor | `articles/new` / `edit/:slug` | 标题、正文、保存、发布 |
| Categories | `/categories` | CRUD、排序 |
| Locales | `/locales` | locale 增删 |
| Portal settings | `/settings` | 域名、主题、SEO |
| Article preview | `/articles/preview/:articleSlug` | 公开预览 |
### 6.7 Campaigns
| 页面 | 路径 | 功能点 |
|---|---|---|
| Live chat campaigns | `/campaigns/live_chat` | 新建、编辑、启停、触发条件 |
| SMS campaigns | `/campaigns/sms` | 空配置降级、表单校验 |
| WhatsApp campaigns | `/campaigns/whatsapp` | provider 缺失提示、模板字段 |
### 6.8 Captain / AI
| 页面 | 路径 | 功能点 |
|---|---|---|
| Assistants | `/captain/:navigationPath` | assistant 列表、新建、切换 |
| FAQs / Responses | `/captain/:assistantId/faqs` | CRUD、搜索、批量删除 |
| Pending responses | `/faqs/pending` | approve/reject |
| Documents | `/documents` | 新建文档、上传、embedding 状态 |
| Tools | `/tools` | custom tool CRUD、参数、鉴权 |
| Scenarios | `/scenarios` | CRUD、启停 |
| Playground | `/playground` | 输入问题、fake AI 回复、错误态 |
| Inboxes | `/inboxes` | assistant 绑定 inbox |
| Settings | `/settings` | 名称、描述、开关 |
| Guardrails | `/settings/guardrails` | 规则保存 |
| Guidelines | `/settings/guidelines` | response guideline 保存 |
### 6.9 Widget / public
| 页面 | 路径 | 功能点 |
|---|---|---|
| Widget home | `/widget?website_token=...#/home` | 可用性、欢迎语、campaign |
| Widget messages | `/widget?website_token=...#/messages` | 客户发消息、附件、emoji、历史 |
| Pre-chat widget | widget pre-chat | 表单字段、必填校验 |
| Article viewer | widget article route | 文章搜索、打开 |
| CSAT public | public CSAT route | 评分、评价提交 |
| Help center public | public portal route | 文章浏览、搜索 |
### 6.10 Global shell / personal / super admin
| 页面 | 路径 | 功能点 |
|---|---|---|
| 侧边栏与全局壳层 | dashboard 任意已登录页 | logo、主导航、收起/展开、未读徽标、当前激活态 |
| 用户菜单 | 侧边栏头像菜单 | 键盘快捷键弹层、更改外观、个人设置入口、退出登录 |
| 账号切换 | 用户菜单中的 account switcher | 不同 account 间切换、URL/accountId 同步、权限不足账号不应泄露 |
| 通知中心 | `/notifications` | 列表、已读、全部已读、跳转回原会话/对象 |
| Super admin 入口 | `/super_admin` | 仅授权用户可见、入口可打开、能返回主应用 |
| Super admin dashboard | super admin 当前实际默认页 | 概览卡片、列表、降级态 |
| Super admin playground | super admin playground | 可进入、表单可操作、无权限用户不可见 |
### 6.11 页面统一验收模板
为了避免“有些页面只看打开,有些页面又测到了保存”,后续 CDP 执行时建议所有页面按页面类型套同一套断言模板。
#### A. 列表页
适用:Dashboard、Contacts、Companies、Teams、Labels、Inboxes、Articles、Documents、Responses、Notifications。
统一断言:
1. 页面可通过真实点击进入。
2. 列表主表格/卡片区有数据或空态文案,不允许白屏。
3. 首屏加载请求全部成功,分页/排序/筛选请求状态码正确。
4. 搜索输入可操作,URL/query 参数与结果同步。
5. 点击一条记录可进入详情或编辑页。
6. 返回列表后筛选条件、滚动位置、tab 状态按产品预期保持。
7. 空态、无结果态、加载态可见。
8. console 无新的未捕获异常。
#### B. 详情页
适用:Conversation、Contact detail、Company detail、Inbox detail、Assistant detail、Portal detail。
统一断言:
1. 必须从列表/入口点击进入,不直接手输内部 URL。
2. 详情页标题、主信息区、侧栏信息区都已渲染。
3. 至少执行 1 个读操作和 1 个写操作(如编辑、切状态、加标签、保存备注)。
4. 保存后 toast/提示正确,刷新后状态仍在。
5. 若详情页包含关联对象(联系人/会话/文档/工具),至少点进 1 个二级对象。
6. 404/已删除/无权限的降级态可见且不崩溃。
#### C. 表单页
适用:创建 Inbox、创建 Agent、Automation、Macro、Custom Attribute、Portal/Article、Campaign、Custom Tool。
统一断言:
1. 必填项校验正确,错误文案清晰。
2. 合法数据可提交,非法数据被前端或后端拒绝。
3. 保存按钮 loading 态和防重复提交正确。
4. 成功后跳转、返回列表或停留当前页的行为符合预期。
5. 编辑已有对象时,默认值回填完整。
6. 离开未保存表单时,如产品定义有提醒则必须触发。
#### D. 报表页
适用:Overview、Agents、Labels、Inboxes、Teams、CSAT、SLA、Bot、Live Reports。
统一断言:
1. 默认时间范围有数据或空态,不白屏。
2. 切换日期范围会重新拉取数据,图表/表格同步变化。
3. 导出、下载、切换维度时请求参数正确。
4. 空数据时仍有结构化占位,而不是 `main` 空白。
5. 指标卡、图表 legend、表格列头与接口字段一致。
#### E. AI / Captain / Copilot 页
适用:Copilot 配置、Assistants、Responses、Documents、Tools、Scenarios、Playground。
统一断言:
1. 页面渲染与列表/表单交互正常。
2. 在 `fake:ai` `ok` 模式下,核心 AI 请求必须可成功走通。
3. 在 `error` / `slow` / `rate_limit` 模式下,错误态必须可见且不假成功。
4. 页面不能泄露明文 API key、Authorization、provider secret。
5. 所有 AI 页面都要同时保留 UI 证据和 `fake:ai` 请求证据。
## 7. Fake channel E2E 脚本化步骤
1. 重置 fake:
```bash
curl -fsS -X POST http://127.0.0.1:9100/api/reset
```
2. 确认配置:
```bash
curl -fsS -X POST http://127.0.0.1:9100/api/config \
-H 'Content-Type: application/json' \
-d '{"webhook_url":"http://127.0.0.1:3000/webhooks/fake/fake_01","auto_reply":true}'
```
3. 入站消息:
```bash
curl -fsS -X POST http://127.0.0.1:9100/api/send \
-H 'Content-Type: application/json' \
-d '{"inbox_identifier":"fake_01","sender_id":"cdp_customer_01","sender_name":"CDP测试客户","content":"CDP 全量测试入站消息"}'
```
4. CDP 断言 dashboard 出现 `CDP测试客户` 和消息内容。
5. CDP 在回复框发送 `收到,正在测试。`
6. fake 断言:
```bash
curl -fsS http://127.0.0.1:9100/api/messages
```
7. CDP 断言 auto reply 回流后同一会话增加客户消息。
## 8. 报告格式
生成到:
```text
docs/qa/reports/2026-07-15-cdp-user-function-report.md
.tmp/cdp-qa/2026-07-15/
```
报告必须包含:
- 服务基线:health、登录账号、测试 account id、fake config。
- 页面矩阵:pass/fail/blocked/skipped。
- 每个失败:复现路径、请求、响应、console、截图、影响等级。
- 数据缺口:缺哪条 seed 导致 blocked。
- fake channel 证据:`/api/messages` 摘要。
- fake:ai 证据:`/api/requests` 摘要。
## 9. 第一轮执行顺序
1. 登录和 WebSocket。
2. fake channel 消息 E2E。
3. Dashboard / conversation 核心路径。
4. Settings 中会影响后续数据的页面:agents、teams、inboxes、labels、attributes。
5. Reports。
6. Help Center / Campaigns / Widget。
7. Captain / Copilot,先用 `fake:ai`。
8. 权限用户回归:agent、custom role。
这样排是为了先证明链路活着,再铺开页面。最省事,也最不容易把数据缺口误判成产品 bug。
## 10. 当前已知高风险 / 预期阻断
以下问题已经在点击驱动验收中出现,后续 CDP 全量测试时应直接按“产品缺陷”记录,不要误归类成数据缺口:
| 项 | 当前现象 | 建议分类 |
|---|---|---|
| fake auto reply 回流 | 最新观测表明平台侧 echo 已回流,但 GoChat 侧会出现“新会话分叉”或“同会话重复/顺序异常渲染”两类缺陷,均应按链路问题记录 | P1 链路缺陷 |
| widget / public 入口 | Website inbox `脚本 / CodePen / 预览 / Chat mode` 已基本穷举,但仍未发现可作为“真实 public/widget 用户入口”验收的稳定点击路径;`CodePen` 外跳也不能替代站内入口 | P1 入口未接通 |
| Help Center 后台三页 | `设置 / 类别 / 语言` 三页在 fresh session 中仍稳定落入空白主区,而 `文章` 列表、编辑、预览链路健康,说明是局部后台渲染缺陷,不是整套帮助中心都坏 | P1 页面主内容未渲染 |
| Super admin 入口 | 从用户菜单点击 `超级管理员控制台` 后,当前 tab 未进入 `/super_admin`;额外新 tab 实为此前 CodePen 的迟到外跳 | P1 入口失效 |
| 通知入口识别 | 顶部无文案按钮当前确认打开的是“新消息/全局发消息”弹层,不是通知中心;真正通知入口在当前健康链路里仍待明确 | P2 验收前置澄清 |
| Captain AI 主链路 | `FAQ / 文档 / Scenarios / 试验场 / 收件箱 / 工具 / 设置 / Guardrails / Response guidelines` 已在健康 click-only 链路下证明可渲染;其中 `试验场` 等真实 AI 效果仍需 `fake:ai` 才能做成功/失败/超时/429 全链路断言 | P1 测试依赖未就绪 |
| Campaign SMS / WhatsApp | 这两页在后续 fresh session 中已能正常渲染内容与空态,不再作为稳定阻断项;后续只需继续做创建/编辑/触发层面的功能验收 | 已从阻断项移除 |
执行原则:
1. 这类问题一旦复现,不再继续用补数据方式兜底。
2. 报告里要保留“点击路径 + 最终 URL + main 区域状态 + 关键请求/快照”四类证据;如果不是空白而是错误态,也要按真实渲染结果记录。
3. widget / public 相关在真正站内入口修好前,只保留入口级验证,不做功能通过判定。
4. Captain 当前不要再按“稳定白屏”预设处理;除了真实 AI 依赖项外,应继续按正常页面矩阵做列表/表单/子页验收。
## 11. 执行前补齐清单(可直接转实施)
这一节把“测试计划”“缺数据”“缺 fake:ai 支撑”压成执行清单,避免后续再来回翻全文。
### 11.1 P0:不补就没法做全量功能验收
| 项 | 需要补什么 | 建议落地方式 | 完成标准 |
|---|---|---|---|
| 登录与权限基线 | 1 个管理员、2 个可登录 agent、1 个 custom role 用户 | `cmd/gochat seed` + 后台补齐 | 三类账号都能真实登录,菜单和权限差异可见 |
| fake 会话主链路 | `fake_01` inbox、fake webhook 正常、会话能进入正确列表 | inbox 配置 + `pnpm fake:start` | 入站、客服回复、auto reply 回流三段都能留证据 |
| 基础会话池 | 至少 6 条不同状态会话,每条 3+ 消息 | fake channel 批量灌数 | open / pending / resolved / snoozed 都能在 UI 中找到 |
| 页面不再只剩空态 | contacts 8+、companies 3+、labels 5、teams 2 | seed 或 UI 创建 | CRM、筛选、报表、分配页面不再被空数据阻断 |
| AI 本地假服务 | `channels/fake-ai` + 根脚本 `fake:ai` | 复用 fake channel 骨架 | Copilot / Captain 能走本地假 provider 成功/失败/超时/429 |
### 11.2 P1:不补会导致“大量页面只能做 render-only”
| 项 | 需要补什么 | 影响页面 |
|---|---|---|
| 回复提效数据 | canned responses 3、macros 3、automation rules 3 | 回复框、设置 CRUD、自动化 |
| 属性与筛选数据 | contact / conversation / company custom attributes 各 2 | CRM、筛选器、自动化条件 |
| 通知与审计数据 | notifications 5、audit logs 5 | 通知中心、审计日志、跳转链路 |
| Help Center 丰富数据 | 1 portal、2 categories、3 articles、2 locales | 后台 portal、public help center、多语言 |
| Campaign 样本 | live chat / sms / whatsapp 至少各 1 个样本 | Campaigns 列表、编辑、启停 |
| Captain 子资源 | documents 3、responses 5、scenarios 2、tools 1 | Captain 子页 CRUD、Playground、文档链路 |
### 11.3 `fake:ai` 的最小验收标准
`fake:ai` 只要做到下面这些,就足够支撑本轮 CDP 页面级功能验收:
| 能力 | 最小要求 | 为什么必须有 |
|---|---|---|
| OpenAI-compatible chat | `POST /v1/chat/completions` 返回固定答复 | Copilot / Captain / reply suggestion 要能成功走通 |
| OpenAI-compatible embeddings | `POST /v1/embeddings` 返回固定维度向量 | Captain documents / embedding 状态要能推进 |
| 模式切换 | `ok / error / slow / rate_limit` | 页面要验证成功、失败、超时、429,而不是只测 happy path |
| 请求留痕 | `GET /api/requests` 可查最近请求摘要 | 报告要拿得到 AI 调用证据,不只截图 |
| 脱敏 | 不记录明文 `Authorization` / `api_key` | 测试日志不能泄露敏感配置 |
### 11.4 推荐实施顺序
1. 先补账号、`fake_01` inbox、基础会话池。
2. 再补 contacts / companies / labels / teams,让 dashboard、CRM、reports 能进入“非空态测试”。
3. 然后补 canned responses / macros / automation / custom attributes,打通 settings 主体 CRUD。
4. 再补 Help Center / campaigns / notifications / audit logs 这些页面群的数据。
5. 最后实现 `fake:ai`,把 Copilot / Captain 从 render-only 升级成真实功能验收。
### 11.5 本轮文档产出对应关系
为避免后续拆任务时语义漂移,这份计划文档里的三类产出边界如下:
| 产出 | 本文档里对应内容 | 后续动作 |
|---|---|---|
| CDP 测试执行规则 | 第 1、2、3、6、7、8、9、10 节 | 后续按此直接做 click-only QA 和 report 追加 |
| 影响全量测试的数据缺口 | 第 4 节 + 本节 11.1 / 11.2 | 后续可拆成 seed、fake、后台初始化任务 |
| AI 测试支撑方案 | 第 5 节 + 本节 11.3 | 后续单独实现 `fake:ai` 并接入 Copilot / Captain |
### 11.6 建议直接拆出的实施任务
为了把“计划文档”顺滑转成“落地任务”,建议按下面 4 个实施包推进:
| 任务包 | 目标 | 建议落地位置 | 完成标志 |
|---|---|---|---|
| A. smoke seed 扩容 | 从单管理员/单会话扩到可支撑大部分页面验收 | `backend/cmd/gochat seed` | 能一次性产出 2 agents、更多会话状态、更多 contacts/companies/labels/teams |
| B. fake channel 批量造数 | 把会话、消息类型、实时事件做成可重复回放 | `channels/fake` | 能按脚本批量制造 open/pending/resolved/snoozed、typing、reply、close |
| C. fake inbox/bootstrap | 让 `fake_01` 不再靠手工散配置维持 | seed + inbox/bootstrap script | fresh 环境里一条命令后即可直接收发 fake webhook |
| D. fake:ai | 给 Copilot/Captain 提供本地稳定 AI 依赖 | `channels/fake-ai` | chat/completions、embeddings、mode switch、request log 全部可用 |
推荐先后顺序:
1. 先做 A + C,保证 dashboard 主链路可测。
2. 再做 B,把 reports / CRM / settings 从“空态浏览”升级成“真实数据验证”。
3. 最后做 D,把 Captain / Copilot 从 render-only 升级成完整 E2E。
## 12. 执行前一页纸清单
这一节是给真正开跑 CDP 全量验收时直接照着做的,避免再从前文抽命令。
### 12.1 环境与服务核对
```bash
curl -fsS http://127.0.0.1:3000/health
curl -fsS http://127.0.0.1:9100/health
curl -fsS http://127.0.0.1:9222/json/version
```
通过标准:
1. backend 返回 200。
2. fake channel 返回 `{"status":"ok","service":"fake-message-platform"}`。
3. CDP debug 口能返回 `webSocketDebuggerUrl`。
### 12.2 现有仓库能力 vs 需要新增的能力
| 类别 | 当前已具备 | 仍需新增 |
|---|---|---|
| 本地消息 fake | 根脚本 `fake:start` / `fake:dev` / `fake:test`,workspace 已纳入 `channels/fake` | 批量造多状态会话、多消息类型、批量造数接口 |
| AI fake | 无 | `channels/fake-ai`、根脚本 `fake:ai*`、workspace 纳入 `channels/fake-ai` |
| smoke seed | `go run ./cmd/gochat seed` 可产出管理员、website inbox、portal/article、1 contact/company/conversation、Captain assistant 基线 | 第二个 agent、fake inbox、更多会话状态、labels/teams、通知/审计、campaign 样本 |
| 页面级执行规则 | 本文档已覆盖 click-only + report sink + fake/fake:ai 证据要求 | 后续按本文档直接执行 |
### 12.3 数据准备责任分层
为了避免后续把所有“补数据”都塞进一个入口,建议这样拆:
| 层 | 负责内容 | 最适合落地位置 |
|---|---|---|
| smoke seed | 可登录账号、website inbox、portal/article、最小 smoke records | `backend/cmd/gochat seed` |
| fake channel | 批量制造会话、消息、typing、close、online/offline | `channels/fake` |
| 后台 UI 自举 | labels、teams、macros、canned responses、custom attributes、部分 portal/campaign | CDP 执行过程中顺手创建 |
| fake AI | chat/completions、embeddings、error/slow/429 模式、请求留痕 | `channels/fake-ai` |
### 12.4 建议先补的 5 个最小 blocker
只要还没补齐下面 5 项,就不要把结果叫“全量实际功能验收完成”:
1. 第二个可登录 agent。
2. `fake_01` 对应 inbox 和 webhook 主链路。
3. 至少 6 条不同状态会话。
4. contacts / companies / labels / teams 的非空数据集。
5. `fake:ai` 本地假 provider。
### 12.5 后续实施拆单建议
如果接下来按实现任务推进,建议就按下面 4 张单拆,不要混成一张“大而全”任务:
1. 扩 `cmd/gochat seed`:补双 agent、更多 CRM / reports / settings 基线数据。
2. 扩 `channels/fake`:补批量造数和多状态回放能力。
3. 加 fake inbox/bootstrap:fresh 环境一条命令后可直接收发 `fake_01`。
4. 新建 `channels/fake-ai`:给 Copilot / Captain 提供本地稳定 AI 依赖。
---
## 13. 第二轮回归补充测试用例
> 更新日期: 2026-07-28
> 基于 §6 页面功能矩阵执行第二轮回归,补充遗漏的功能测试点。
### 13.1 会话(Conversation)操作补充
| # | 操作 | API 端点 | 验证结果 | 说明 |
|---|------|----------|---------|------|
| C1 | 客服发送消息 | POST /conversations/:id/messages (outgoing) | ✅ | 回复消息持久化到 DB |
| C2 | 私密备注 | POST /conversations/:id/messages (private_note) | ✅ | private flag 正确处理 |
| C3 | 会话详情 | GET /conversations/:id | ✅ | 含消息列表 |
| C4 | 切换优先级 | POST /conversations/:id/toggle_priority | ✅ | 无请求体 |
| C5 | 设置优先级 | PATCH /conversations/:id/priority | ✅ | body: {"priority":"high"} |
| C6 | 状态流转: resolve | POST /conversations/:id/toggle_status | ✅ | status=resolved |
| C7 | 状态流转: reopen | POST /conversations/:id/toggle_status | ✅ | status=open |
| C8 | 状态流转: snooze | POST /conversations/:id/toggle_status | ✅ | status=snoozed |
| C9 | 消息已读 | POST /conversations/:id/update_last_seen | ✅ | |
| C10 | 静音/取消静音 | POST /conversations/:id/mute & /unmute | ✅ | 两次调用均成功 |
| C11 | 标签增删 | POST /conversations/:id/labels | ✅ | body: {"labels":["tag1"]} |
| C12 | 附件列表 | GET /conversations/:id/attachments | ✅ | 返回空列表 meta |
| C13 | 输入状态(API) | POST /toggle_typing_status | ✅ | typing_status=on/off |
| C14 | 文件上传 | POST multipart /messages + file | ✅ | 见下方 §13.6 |
### 13.2 静态 Auto-Reply(新增功能点)
| # | 操作 | 端点 | 验证结果 | 说明 |
|---|------|------|---------|------|
| AR1 | 创建规则 | POST /captain/auto_reply_rules (mode=static) | ✅ | 返回 id, status=draft |
| AR2 | 激活规则 | PUT /:rule_id (status=active) | ✅ | |
| AR3 | 条件匹配 | content contains "hello" | ✅ | 自动回复生效 |
| AR4 | 多规则匹配 | 按 priority 降序执行首条匹配 | ✅ | |
| AR5 | 回复内容 | 静态回复文本 | ✅ | "Hello! How can we help..." |
| AR6 | Sender 身份 | AgentBot (sender_type=AgentBot) | ✅ | 通过 botInboxRepo 解析 |
### 13.3 LLM Auto-Reply(新增功能点)
| # | 操作 | 条件 | 验证结果 | 说明 |
|---|------|------|---------|------|
| LR1 | Provider 配置 | COPILOT_PROVIDER_CONFIG (installation_configs) | ✅ | openai_compatible + deepseek-v4-flash |
| LR2 | 聊天 API | ChatCompletion | ✅ | 10.58.144.6:2014/v1 可达 |
| LR3 | 创建 LLM 规则 | mode=llm | ✅ | 无 response_text |
| LR4 | LLM 生成回复 | content matches -> LLM call | ✅ | deepseek 返回自然语言 |
### 13.4 AgentBot(新增功能点)
| # | 操作 | 端点 | 验证结果 |
|---|------|------|---------|
| AB1 | 创建 | POST /agent_bots | ✅ |
| AB2 | 查询列表 | GET /agent_bots | ✅ |
| AB3 | 详情 | GET /agent_bots/:id | ✅ |
| AB4 | 更新 | PATCH /agent_bots/:id | ✅ |
| AB5 | 绑定到 Inbox | POST /inboxes/:id/set_agent_bot | ✅ |
| AB6 | Inbox 查询绑定 | GET /agent_bot_inboxes/by_bot?agent_bot_id=X | ❌ 404 路由待注册 |
### 13.5 前端渲染时序(已知 BUG 验证)
| # | 场景 | 状态 | 说明 |
|---|------|------|------|
| F1 | localStorage auth token 同步 | ✅ | BUG-W2 修复验证通过 |
| F2 | Dashboard 自动加载会话 | ❌ | ChatList 组件 onMounted 有时不发请求 |
| F3 | API 代理可达 | ✅ | 通过 Vite proxy `/api` → :3000 正常 |
| F4 | 手动 dispatch 加载 | ✅ | `store.dispatch('fetchAllConversations')` 成功后渲染正常 |
### 13.6 文件上传(Round 7 后修复验证)
| # | 步骤 | 证据 | 状态 |
|---|------|------|------|
| U1 | POST multipart + file → 200 | msg_id=126 attachments=1 | ✅ |
| U2 | Metadata jsonb 修复 | attachment.metadata = "{}" 非空字符串 | ✅ |
| U3 | 搜索结果含附件消息 | GET search 返回正确 | ✅ |
### 13.7 已知后端未实现端点
| 端点 | 方法 | 说明 | 状态 |
|------|------|------|------|
| /auth/login | POST | Chatwoot 使用 /auth/sign_in | 差异(非缺陷) |
| /api/v1/accounts/:id/conversation_workflows | GET | 路由未注册 | 未实现 (P3), 需完整 CRUD handler |
| /api/v1/accounts/:id/reports/overview | GET | 前端仅 Vue Router 路径,API 使用 /reports?metric=... | 非缺陷 |
### 13.7b 已修复端点
| 端点 | 方法 | 修复内容 | 状态 |
|------|------|---------|------|
| /api/v1/widget/config | GET | 新增 GET 路由(原仅 POST) | ✅ |
| /api/v1/widget/config | POST | widget.html 将 GET→POST 调用,发送 JSON body | ✅ |
| /api/v1/accounts/:id/copilot/config | GET | 确认正确路径为 /copilot/config 非 /copilot_config | ✅ 已验证 |
| /api/v1/accounts/:id/agent_bot_inboxes/ | GET | 正确路径为 ?inbox_id=X(非 /by_inbox) | ✅ 已验证 |
### 13.8 补充关键断言模板
#### D. Sender 身份断言模板
适用:所有 auto-reply / bot / Captain 发出的消息。
统一断言:
1. 消息 `sender_type = "AgentBot"`(非 "user" 或 "contact")
2. `sender_id` 指向有效的 `agent_bots` 记录
3. inbox 必须在 `agent_bot_inboxes` 表中有 active 绑定
4. 前端 UI 中消息头像/名称显示为 bot 名称而非 agent 头像
#### E. AI Provider 连通性断言模板
适用:Copilot、Captain、LLM auto-reply 等调用外部 AI API 的功能。
统一断言:
1. Provider 配置在 `installation_configs` 表中完整
2. ChatCompletion 调用返回 200 + choices > 0
3. 失败时有明确错误日志(非静默吞掉)
4. 网络延迟/超时时有重试机制和退避
5. 生产环境 API key 不写入代码或 YAML 配置