From 6b42853c0b105a70fa35b8804fe72c3f3d8269f1 Mon Sep 17 00:00:00 2001 From: Rogee Date: Mon, 13 Jul 2026 10:47:48 +0800 Subject: [PATCH] update doc --- docs/QA_REPORT_2026-07-10_cdp_round6.md | 243 ------------ docs/qa/2026-07-09-manual-testing-guide.md | 393 ------------------- docs/qa/2026-07-09-qa-report-round5.md | 250 ------------ docs/qa/2026-07-09-test-plan-round5.md | 435 --------------------- docs/qa/2026-07-10-test-plan-round7.md | 304 ++++++++++++++ 5 files changed, 304 insertions(+), 1321 deletions(-) delete mode 100644 docs/QA_REPORT_2026-07-10_cdp_round6.md delete mode 100644 docs/qa/2026-07-09-manual-testing-guide.md delete mode 100644 docs/qa/2026-07-09-qa-report-round5.md delete mode 100644 docs/qa/2026-07-09-test-plan-round5.md create mode 100644 docs/qa/2026-07-10-test-plan-round7.md diff --git a/docs/QA_REPORT_2026-07-10_cdp_round6.md b/docs/QA_REPORT_2026-07-10_cdp_round6.md deleted file mode 100644 index 16e5aac6..00000000 --- a/docs/QA_REPORT_2026-07-10_cdp_round6.md +++ /dev/null @@ -1,243 +0,0 @@ -# QA Report Round 6 — Worker Redis Stream Migration Verification - -> Date: 2026-07-10 -> Commit tested: 762de6a (DB polling → Redis Stream dispatch) -> Test plan: `docs/QA_TEST_PLAN_2026-07-10_cdp_round6.md` - ---- - -## 1. Summary - -| Metric | Value | -|--------|-------| -| Pages tested via CDP | 11 pages (Dashboard, Conversation, Contacts, Reports×4, Settings×4, Help Center) | -| Worker Redis paths verified | 6 job types across 6 queues | -| Pass | 11 pages + 6 worker paths | -| Findings | 4 new findings (1 P1, 3 P3) | -| WebSocket /cable | Connected, stable, no 401 | -| Overall verdict | Redis Stream migration PASSED — all user-visible functionality intact; 1 pre-existing P1 bug surfaced | - ---- - -## 2. Worker Redis Stream Migration Verification - -### 2.1 Redis Consumer Group Init - -All 10 queue streams created with `gochat-workers` consumer group: -`default`, `high`, `medium`, `low`, `events`, `automation`, `search`, `scheduled_jobs`, `deferred` — all with consumer group `gochat-workers`, pending=0. - -`purgable` stream: `ERR no such key` — no job has ever been enqueued to this queue, so the stream was never created by `XGROUP CREATE MKSTREAM`. This is expected behavior (stream is created lazily on first `XADD`). - -### 2.2 Concurrency - -- `config.dev.yaml` sets `concurrency: 5` -- Backend uses `NewWorkerPoolWithOptions(db, WithWorkerCount(cfg.Worker.Concurrency))` -- Redis `XINFO CONSUMERS` shows 1 consumer named `worker-{nanoseconds}` -- **Root cause:** All goroutines share the same `wp.workerID` (set once at pool creation), so Redis groups them as a single consumer -- **Impact:** Concurrency IS working (5 goroutines launched), but observability is limited — cannot distinguish per-goroutine activity in Redis -- **Severity:** P3 (observability, no functional impact) - -### 2.3 Core Job Path Verification - -| Job Type | Queue | Triggered By | DB Status | Redis Stream | Verdict | -|----------|-------|-------------|-----------|--------------|---------| -| `message:send_reply` | high | Agent reply via API (conversation 8) | completed, attempts=1 | XLEN=1, pending=0 | PASS | -| `event:dispatch_async` | events | Triggered by message reply | completed, attempts=1 | XLEN=0, pending=0 | PASS | -| `search:index` | search | Contact/message changes | dead, attempts=3 | XLEN=6, pending=0 | PASS (dead = Meilisearch not configured, known) | -| `conversation:reopen_snoozed` | low | Sweep (scheduled maintenance) | completed, attempts=1 | XLEN=2, pending=0 | PASS | -| `account:conversations_resolution_scheduler` | scheduled_jobs | Scheduled sweep | completed, attempts=1 | XLEN=11, pending=0 | PASS | -| `sla:trigger_accounts` (delayed) | scheduled_jobs | Scheduled (5min cycle) | completed after sweep | pending=0 | PASS | - -### 2.4 Delayed Job Sweep Verification - -- Job `sla:trigger_accounts` (id=1266) had `scheduled_at = 2026-07-10 06:22:33 UTC` -- At `06:21:51 UTC` the job was `queued` (not yet due) -- At `06:23:00 UTC` the job was `completed` (sweep picked it up after maturity) -- Sweep interval = 15s (dev config) — confirmed via backend log showing `worker.go:603` sweep query every ~15s -- `RequeueStaleJobs` also running in sweep loop (`worker.go:375`) — confirmed via backend logs - -### 2.5 Message Send End-to-End (Browser Verified) - -1. API call: `POST /api/v1/accounts/1/conversations/8/messages` with content "Redis Stream迁移测试-客服回复" -2. API returned message id=113, status=sent -3. DB job id=1263: `message:send_reply`, status=completed, queue=high, attempts=1 -4. Redis high stream: XLEN=1, pending=0 (consumed and ACKed) -5. Browser: conversation 8 detail view shows 3 messages, including "Redis Stream迁移测试-客服回复" with timestamp "Jul 10, 2:16 PM" -6. No console errors on conversation detail page - -**Verdict:** Redis Stream dispatch path fully functional for message sending. - -### 2.6 FakeMessagePlatform Inbound (Blocked by BUG-F) - -- FakeMessagePlatform sends to `/webhooks/fake/fake_test_1` with `X-Fake-Token: fake_test_token` -- GoChat returns `{"status":"ignored"}` — message not persisted -- Root cause: `fake_webhook.go:167` uses `channel_config @> '{"identifier":"..."}'` but `channel_config` is a TEXT column, not JSONB — the `@>` operator fails -- This is a **pre-existing bug** (not introduced by the Redis migration), confirmed by the fact that the last successful fake webhook was in round 5 before this column type issue would have manifested -- See BUG-F below for details - ---- - -## 3. WebSocket /cable Verification - -- After login: `/cable` connected successfully -- Backend logs show `ws: message command from user=1, data={"action":"update_presence"}` every ~1-2s (heartbeat) -- No `ws: authentication failed` in backend logs -- No reconnect storm — WS connection stable for entire test duration (~30 min) -- Backend log: `ws hub: client unregistered` appears only on page navigation (expected) - -**Verdict:** WebSocket /cable healthy. Real-time features functional. - ---- - -## 4. Per-Page Results - -| # | Page | Click Path | Result | Findings | -|---|------|------------|--------|----------| -| 1 | Dashboard | (post-login) | PASS | Clean console, conversation list loads, sidebar visible | -| 2 | Conversation detail | Click "测试客户A" conversation | PASS | Messages render including new reply; 3 onClose warnings (allowlisted) | -| 3 | Contacts | Sidebar: 联系人 | PASS | 8 contacts loaded, edit forms visible; clean console | -| 4 | Reports Overview | Sidebar: 报告 | PASS | Stats cards, charts, agent table, team stats; clean console | -| 5 | Reports SLA | Reports sub-tab: SLA | PASS | SLA metrics (100% hit rate), "no SLA applied conversations"; clean console | -| 6 | Reports Bot | Reports sub-tab: 机器人 | PASS | BUG-A regression: sidebar nav from within Reports WORKS (FIXED) | -| 7 | Settings General | Sidebar: 设置 | PASS | Account settings form loads; clean console | -| 8 | Settings Agent Bots | 设置 sub-link: 机器人 | PASS | BUG-2 regression: sidebar click navigates correctly (FIXED) | -| 9 | Settings Inboxes | 设置 sub-link: 收件箱 | PASS | Inbox list loads (shows 1 of 2 inboxes — see BUG-H) | -| 10 | Inbox Pre-chat Form | Inbox settings → 预聊天表单 tab | PASS | BUG-C regression: no Vue error on null pre_chat_form_options (FIXED) | -| 11 | Help Center (Portals) | Sidebar: 帮助中心 | PASS | Articles/categories/locales/settings tabs visible; clean console | -| 12 | Activity (Campaigns) | Sidebar: 活动 | PASS | BUG-1 regression: route resolves to /campaigns/live_chat (FIXED); 1 new Vue Router warning (BUG-G) | - ---- - -## 5. Findings - -### BUG-F (P1) — Fake webhook lookupInbox uses JSONB @> on TEXT column - -- **Severity:** P1 -- **Symptom:** All fake channel webhooks return `{"status":"ignored"}` — no inbound messages persisted. -- **Root cause:** `backend/internal/handler/webhook/fake_webhook.go:167` constructs the query: - ```sql - channel_type = 'fake' AND channel_config @> '{"identifier":"fake_test_1"}' - ``` - But `inboxes.channel_config` is a `TEXT` column (confirmed via `\d inboxes`), not `JSONB`. The `@>` operator does not exist for `TEXT @> unknown`, causing a PostgreSQL error that gets caught and returns "inbox not found". -- **Evidence:** - - Direct psql: `SELECT ... WHERE channel_config @> '{"identifier":"fake_test_1"}'` → `ERROR: operator does not exist: text @> unknown` - - With cast: `SELECT ... WHERE channel_config::jsonb @> '{"identifier":"fake_test_1"}'` → returns inbox id=3 correctly - - curl to webhook: `{"status":"ignored"}` -- **Affected files:** `backend/internal/handler/webhook/fake_webhook.go:167` -- **Fix direction:** Either (a) cast to jsonb in the query: `channel_config::jsonb @> ...`, or (b) change the `channel_config` column type to `JSONB` via a migration. Option (a) is the minimal fix; option (b) is the correct fix (JSONB is the right type for structured config). -- **Status:** NEW in round 6. Pre-existing (not introduced by Redis migration), but first time surfaced via CDP testing. - -### BUG-G (P3) — Vue Router history.state warning on Activity page - -- **Severity:** P3 -- **Symptom:** Console warning: `[Vue Router warn]: history.state seems to have been manually replaced without preserving the necessary values.` -- **Context:** Appears when navigating to the Activity (Campaigns) page via sidebar click. -- **Root cause:** Something in the route navigation chain calls `history.replaceState` without preserving the existing state object. Likely related to the campaign list component or a router guard. -- **Affected files:** Likely in `frontend/app/javascript/dashboard/routes/` or campaign-related route guards. -- **Status:** NEW in round 6. No functional impact — page loads correctly. - -### BUG-H (P3) — Inbox list page shows only 1 of 2 inboxes - -- **Severity:** P3 -- **Symptom:** Settings → Inboxes list shows "1 inbox" (Test Website Inbox only), but DB has 2 enabled fake+web inboxes (id=1 web_widget, id=3 fake). -- **Context:** The Fake Test Inbox (id=3) is enabled (`enabled=true`, `deleted_at=NULL`) but doesn't appear in the UI list. -- **Root cause:** Unknown — the inbox list API may filter by account_id or the frontend may not render fake-type inboxes in the settings list. Needs investigation of the inbox list API endpoint and frontend component. -- **Affected files:** Backend inbox list handler or frontend `SettingsInboxList.vue` equivalent. -- **Status:** NEW in round 6. Pre-existing (not introduced by Redis migration). - -### BUG-I (P3) — All worker goroutines share same Redis consumer name - -- **Severity:** P3 (observability) -- **Symptom:** Redis `XINFO CONSUMERS` shows 1 consumer even though `concurrency=5`. -- **Root cause:** `WorkerPool.workerID` is set once at pool creation (`fmt.Sprintf("worker-%d", time.Now().UnixNano())`) and shared by all goroutines. `Start()` launches `workerCount` goroutines all calling `go wp.run(ctx)`, but they all use `wp.workerID` in `XReadGroup.Consumer`. -- **Affected files:** `backend/internal/worker/worker.go:62` (workerID assignment), `worker.go:398` (XReadGroup Consumer field) -- **Fix direction:** Generate a unique consumer ID per goroutine (e.g., `fmt.Sprintf("worker-%d-%d", time.Now().UnixNano(), i)`) and pass it to `run()`. -- **Status:** NEW in round 6. Introduced by the Redis migration commit (762de6a). - ---- - -## 6. Known Issues Re-Verification - -| Bug | Description | Status (Round 6) | Evidence | -|-----|-------------|-------------------|----------| -| BUG-1 | Activity route missing | FIXED | Sidebar "活动" → `/campaigns/live_chat` loads correctly | -| BUG-2 | Bots sidebar link not navigating | FIXED | Settings → "机器人" → `/settings/agent-bots` loads | -| BUG-3/B | intlify empty key warnings | NOT TESTED | Did not create a label in this round | -| BUG-A | Bot Reports sidebar link from Reports | FIXED | Reports SLA → "机器人" → `/reports/bot` navigates | -| BUG-C | Pre-chat Form null error | FIXED | Inbox settings → 预聊天表单 tab → no Vue error | -| BUG-D | ChannelItem isActive whitelist (fake) | NOT TESTED | Did not navigate to inbox creation page | -| BUG-E | inbox_members not written | NOT TESTED | Did not create a new inbox | -| BUG-7 | 6 missing tables / 500 | FIXED | All 6 endpoints return 200 | - ---- - -## 7. Backend Errors Observed - -- No HTTP 500s during testing -- No panics -- No WebSocket authentication failures -- GORM logger noise from sweep queries (expected — worker.go:603 SELECT every 15s) -- `XGroupCreate` warnings did not appear (consumer groups created successfully on first Start) - ---- - -## 8. Redis Worker State Snapshot - -``` -# Consumer Groups (all 10 queues) -gochat:jobs:default → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:high → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:medium → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:low → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:events → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:automation → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:search → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:scheduled_jobs → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:deferred → group: gochat-workers, consumers: 1, pending: 0 -gochat:jobs:purgable → ERR no such key (lazy creation, expected) -``` - ---- - -## 9. Testing Notes - -### What was tested -- Backend startup with Redis worker config (config.dev.yaml: concurrency=5, sweep=15s) -- DB schema integrity (6 endpoints return 200) -- Redis consumer group creation (10 queues) -- Message send reply via API → Redis high queue → worker → DB completed -- Event dispatch async → Redis events queue → completed -- Delayed job (sla:trigger_accounts) sweep pickup after maturity -- Conversation maintenance jobs → completed via sweep -- WebSocket /cable stability (30 min, no reconnect storm) -- 11 CDP browser pages with click navigation + console error capture -- 5 known bug regressions (BUG-1, BUG-2, BUG-A, BUG-C, BUG-7 — all FIXED) - -### What was skipped / not tested -- 28-page full matrix (tested 11 key pages — browser tools had session timeout issues mid-run) -- FakeMessagePlatform inbound message flow (blocked by BUG-F — webhook lookupInbox fails on TEXT column) -- FakeMessagePlatform outbound message delivery (dependent on inbound working first) -- Cross-tab real-time delivery (Phase 5 skipped due to iteration limit) -- Direct WS test via websocat (Phase 5 skipped) -- BUG-3/B (intlify empty key) — did not create a label -- BUG-D (ChannelItem isActive) — did not navigate to inbox creation page -- BUG-E (inbox_members) — did not create a new inbox -- Profile page — not reached -- Settings: Teams, Labels, Custom Attributes, Automation, Macros, Canned Responses, Integrations, Conversation Workflow — not reached - -### Blockers -- BUG-F (P1): Fake webhook `lookupInbox` fails because `channel_config` is TEXT, not JSONB. This blocks FakeMessagePlatform integration testing but does NOT affect the Redis Stream migration verification — the migration is about job dispatch, not webhook processing. - ---- - -## 10. Conclusion - -The Redis Stream migration (commit 762de6a) is **functionally verified**: - -1. Worker jobs dispatch correctly through Redis Streams (XADD → XREADGROUP → DB claim → perform → XACK) -2. All tested job types complete successfully through the Redis path -3. Delayed jobs are picked up by the sweep mechanism when they mature -4. The concurrency fix (NewWorkerPoolWithOptions with WithWorkerCount) is applied — 5 goroutines are launched -5. No user-visible functionality regression — pages load, messages send, WebSocket connects, no new console errors from the migration itself - -The migration is safe to keep. The findings (BUG-F through BUG-I) are either pre-existing or minor observability issues that should be addressed separately. diff --git a/docs/qa/2026-07-09-manual-testing-guide.md b/docs/qa/2026-07-09-manual-testing-guide.md deleted file mode 100644 index 50f9f76a..00000000 --- a/docs/qa/2026-07-09-manual-testing-guide.md +++ /dev/null @@ -1,393 +0,0 @@ -# FakeMessagePlatform 手工测试指南 - -> 基于 `docs/plans/2026-07-09-brainstorming-fake-message-platform.md` 和 `docs/qa/2026-07-09-test-plan-round5.md` -> 适用于开发者本地手工验证 FakeMessagePlatform 全链路消息流。 - ---- - -## 前置条件 - -1. PostgreSQL 16 + pgvector 运行在 `localhost:5444`,数据库 `gochat_dev` 已初始化 -2. Redis 运行在 `localhost:6379` -3. Go 1.24+ 和 Node.js 20+ / pnpm 10+ 已安装 -4. 仓库根目录执行过 `pnpm install` -5. 种子数据已加载(`admin@gochat.local / changeme` 账号存在) - ---- - -## Step 1:启动三个服务 - -打开三个终端窗口: - -```bash -# 终端 1:GoChat 后端 (:3000) -cd /home/yanghao05/Projects/gochat -export GOROOT=/usr/lib/go-1.24 && export PATH=$GOROOT/bin:/home/yanghao05/.local/node-v22.20.0-linux-x64/bin:$PATH -export GOMODCACHE=/home/yanghao05/go/pkg/mod -pnpm dev:backend - -# 终端 2:前端 Vite (:3036) -cd /home/yanghao05/Projects/gochat -export PATH="/home/yanghao05/.local/node-v22.20.0-linux-x64/bin:$PATH" -cd frontend && npx vite --port 3036 - -# 终端 3:FakeMessagePlatform (:9100) -cd /home/yanghao05/Projects/gochat -export PATH="/home/yanghao05/.local/node-v22.20.0-linux-x64/bin:$PATH" -cd channels/fake && npx tsx src/index.ts -``` - -等待 10-15 秒,然后验证三个服务健康: - -```bash -curl http://127.0.0.1:3000/health # 预期: {"status":"ok",...} -curl -o /dev/null -w '%{http_code}' http://127.0.0.1:3036/ # 预期: 200 -curl http://127.0.0.1:9100/health # 预期: {"status":"ok","service":"fake-message-platform"} -``` - -三个都通过才能继续。 - ---- - -## Step 2:通过前端 UI 创建 Fake 渠道 Inbox - -1. 浏览器打开 `http://127.0.0.1:3036/app/login` -2. 登录:`admin@gochat.local` / `changeme` -3. 左侧栏点击「设置」展开子菜单 -4. 点击「收件箱」 -5. 点击「添加收件箱」 -6. 在渠道选择页面找到「Fake 测试平台」卡片,点击 -7. 填写表单: - - 频道名称:`Fake Test Inbox` - - 标识符 (Identifier):`fake_test_1` - - Webhook URL:`http://127.0.0.1:9100/receive` - - Token:`fake_test_token` -8. 点击「创建 Fake 频道」 -9. 在 agent 分配页面添加 admin 到此 inbox -10. 返回收件箱列表,确认 "Fake Test Inbox" 出现在列表中 - -如果前端 UI 因浏览器问题不稳定,可以用 API 替代: - -```bash -curl -X POST http://127.0.0.1:3000/api/v1/accounts/1/inboxes \ - -H "Content-Type: application/json" \ - -H "X-User-ID: 1" -H "X-Account-ID: 1" \ - -d '{ - "name": "Fake Test Inbox", - "channel": { - "type": "fake", - "identifier": "fake_test_1", - "webhook_url": "http://127.0.0.1:9100/receive", - "token": "fake_test_token" - } - }' -``` - -预期返回 JSON 中 `channel_type: "fake"`,`id: 2`(或更大)。 - ---- - -## Step 3:配置 FakeMessagePlatform 的 GoChat webhook URL - -FakeMessagePlatform 需要知道 GoChat 的 webhook 端点和 token: - -```bash -curl -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_test_1","token":"fake_test_token"}' -``` - -预期返回:`{"status":"ok"}` - -> 如果启动 FakeMessagePlatform 时已经设置了环境变量 `GOCHAT_WEBHOOK_URL` 和 `GOCHAT_FAKE_TOKEN`, -> 则此步可跳过。但默认 URL 用的是 `fake_inbox_1`,需要改成你实际创建的 identifier。 - ---- - -## Step 4:连通性测试 - -发送一条测试消息,验证 FakeMessagePlatform → GoChat 的链路通畅: - -```bash -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"smoke_test","sender_name":"连通性测试","content":"ping"}' -``` - -预期返回: -```json -{"status":"sent","message_id":"fake_msg_...","gochat_status":200} -``` - -检查 FakeMessagePlatform 状态: - -```bash -curl http://127.0.0.1:9100/api/status -``` - -预期 `total_sent >= 1`。 - -如果 `gochat_status` 不是 200,说明 webhook 未正确接收。检查: -- FakeMessagePlatform 的 webhook_url 是否指向正确的 identifier -- GoChat 后端日志是否出现 "Fake webhook received"(注意:后端 worker pool 日志很多,需要过滤查找) - ---- - -## Step 5:入站消息 — 客户发消息 → GoChat 创建会话 - -模拟客户"测试客户A"发一条消息: - -```bash -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","sender_name":"测试客户A","content":"你好,我需要帮助"}' -``` - -验证: -1. 前端 Dashboard 应出现新会话(如果前端打开了的话) -2. 通过 API 确认会话和消息已创建: - -```bash -# 查看最新会话(替换 ID 为实际的会话 ID) -curl -s "http://127.0.0.1:3000/api/v1/accounts/1/conversations/4" \ - -H "X-User-ID: 1" -H "X-Account-ID: 1" | python3 -m json.tool | head -30 -``` - -3. 或直接查数据库确认: - -```bash -PGPASSWORD=xiha02 psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev -c \ - "SELECT id, content, sender_type, source_id FROM messages WHERE inbox_id=2 ORDER BY id DESC LIMIT 5" -``` - -预期:看到 content="你好,我需要帮助",sender_type="contact",source_id 以 "fake_msg_" 开头。 - ---- - -## Step 6:出站消息 — 客服回复 → FakeMessagePlatform 收到 - -模拟客服在会话中回复(替换 `4` 为实际的会话 ID): - -```bash -curl -X POST "http://127.0.0.1:3000/api/v1/accounts/1/conversations/4/messages" \ - -H "X-User-ID: 1" -H "X-Account-ID: 1" \ - -H "Content-Type: application/json" \ - -d '{"content":"您好,有什么可以帮您?","message_type":"outgoing","private":false}' -``` - -验证 FakeMessagePlatform 收到了出站消息: - -```bash -curl http://127.0.0.1:9100/api/messages?inbox_identifier=fake_test_1 -``` - -预期返回的 `received` 数组中包含 content="您好,有什么可以帮您?",sender.type 为 "agent"。 - -这一步验证了完整的双向消息流: -``` -客户消息 → FakeMsgPlatform → GoChat webhook → 创建会话/消息 -客服回复 → GoChat API → FakeProvider.SendMessage → POST /receive → FakeMsgPlatform 存储 -``` - ---- - -## Step 7:多客户并发会话 - -模拟两个不同客户同时发消息: - -```bash -# 客户 B -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_002","sender_name":"测试客户B","content":"退款咨询"}' - -# 客户 C -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_003","sender_name":"测试客户C","content":"技术支持"}' -``` - -验证创建了独立的会话: - -```bash -PGPASSWORD=xiha02 psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev -c \ - "SELECT c.id, c.status, ct.name FROM conversations c JOIN contacts ct ON c.contact_id=ct.id WHERE c.inbox_id=2 ORDER BY c.id" -``` - -预期:每个 sender_id 对应一个独立的会话和联系人。 - ---- - -## Step 8:打字状态指示 - -模拟客户正在打字: - -```bash -# 开始打字 -curl -X POST http://127.0.0.1:9100/api/typing \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","typing":true}' - -# 停止打字 -curl -X POST http://127.0.0.1:9100/api/typing \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","typing":false}' -``` - -预期:两次请求都返回 `{"status":"sent","typing":true/false}`。 - ---- - -## Step 9:关闭聊天窗口 - -模拟客户关闭聊天窗口(发送 session.end 事件): - -```bash -curl -X POST http://127.0.0.1:9100/api/close \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001"}' -``` - -验证 GoChat 创建了 "[session ended]" 系统消息: - -```bash -PGPASSWORD=xiha02 psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev -c \ - "SELECT id, content, source_id FROM messages WHERE content='[session ended]' AND inbox_id=2" -``` - -预期:至少一条记录,source_id 以 "fake_close_" 开头。 - ---- - -## Step 10:消息附件 - -发送带图片附件的消息: - -```bash -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{ - "inbox_identifier":"fake_test_1", - "sender_id":"customer_001", - "sender_name":"测试客户A", - "content":"请看这张截图", - "content_type":"image", - "attachments":[{ - "url":"http://example.com/screenshot.png", - "content_type":"image/png", - "filename":"screenshot.png", - "file_size":102400 - }] - }' -``` - -验证消息以 image 类型创建: - -```bash -PGPASSWORD=xiha02 psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev -c \ - "SELECT id, content_type, content_attributes FROM messages WHERE inbox_id=2 AND content_type='image' ORDER BY id DESC LIMIT 3" -``` - -预期:content_type 为 "image",content_attributes 中包含附件 URL。 - ---- - -## Step 11:客服上下线状态(FakeMessagePlatform 侧记录) - -模拟客服上线和下线(这些是 FakeMessagePlatform 内存中记录的状态,供测试脚本查询): - -```bash -# 客服上线 -curl -X POST http://127.0.0.1:9100/api/agent/online \ - -H 'Content-Type: application/json' \ - -d '{"agent_id":"1","agent_name":"Admin"}' - -# 查看状态 -curl http://127.0.0.1:9100/api/status -# 预期:online_agents 中包含 agent_id="1" - -# 客服下线 -curl -X POST http://127.0.0.1:9100/api/agent/offline \ - -H 'Content-Type: application/json' \ - -d '{"agent_id":"1"}' - -# 再次查看状态 -curl http://127.0.0.1:9100/api/status -# 预期:online_agents 为空 -``` - ---- - -## Step 12:重置状态(可选) - -在每次测试前重置 FakeMessagePlatform 的内存状态: - -```bash -curl -X POST http://127.0.0.1:9100/api/reset -``` - -预期返回 `{"status":"ok"}`,之后 `/api/status` 显示所有计数为 0。 - ---- - -## 验证清单 - -- [ ] 三个服务全部启动且健康检查通过 -- [ ] Fake Inbox 成功创建(channel_type=fake) -- [ ] FakeMessagePlatform webhook URL 已正确配置 -- [ ] 连通性测试:发消息 → GoChat 返回 200 -- [ ] 入站消息:客户发消息 → 创建 Contact + Conversation + Message -- [ ] 出站消息:客服回复 → FakeMessagePlatform /receive 收到 -- [ ] 多客户并发:每个客户独立会话 -- [ ] 打字状态:typing true/false 事件成功发送 -- [ ] 会话关闭:"[session ended]" 消息创建 -- [ ] 消息附件:image 类型消息正确创建 -- [ ] 客服上下线:FakeMessagePlatform 状态正确记录 - ---- - -## 常见问题排查 - -### Q: FakeMessagePlatform 发消息返回 gochat_status 非 200 - -检查 FakeMessagePlatform 的 webhook URL: -```bash -curl http://127.0.0.1:9100/api/status -``` -确认 webhook_url 指向 `http://127.0.0.1:3000/webhooks/fake/<你的identifier>`。 - -### Q: GoChat webhook 返回 200 但没有创建会话 - -FakeWebhookHandler 在出错时也返回 200(遵循 webhook 惯例)。检查后端日志: -```bash -# 在后端终端中查找 "Fake webhook" 相关日志 -# 注意 worker pool 的 "record not found" 日志是正常噪音,不影响消息流 -``` -或直接查数据库确认消息是否已写入。 - -### Q: 客服回复没有到达 FakeMessagePlatform - -确认 Fake Inbox 的 channel_config 中 webhook_url 指向 FakeMessagePlatform 的 /receive: -```bash -PGPASSWORD=xiha02 psql -h 127.0.0.1 -p 5444 -U postgres -d gochat_dev -c \ - "SELECT channel_config FROM inboxes WHERE channel_type='fake'" -``` -预期 webhook_url 为 `http://127.0.0.1:9100/receive`。 - -### Q: Meilisearch 连接失败 - -后端日志中可能出现 `meilisearch index document: ... connection refused`。这是因为 Meilisearch 未运行,搜索索引后台 job 会失败但**不影响消息收发**。如需完整搜索功能: -```bash -# 可选:启动 Meilisearch -cd /home/yanghao05/Projects/gochat/deploy/quickstart && docker compose up -d meilisearch -``` - ---- - -## 停止服务 - -测试完成后,在各终端按 Ctrl+C 停止服务。或批量停止: - -```bash -kill $(lsof -t -i:3000 -i:3036 -i:9100) 2>/dev/null -``` diff --git a/docs/qa/2026-07-09-qa-report-round5.md b/docs/qa/2026-07-09-qa-report-round5.md deleted file mode 100644 index f1ebf458..00000000 --- a/docs/qa/2026-07-09-qa-report-round5.md +++ /dev/null @@ -1,250 +0,0 @@ -# QA Report — Round 5: FakeMessagePlatform 全链路测试 - -**日期:** 2026-07-09 -**环境:** backend (:3000) + frontend Vite (:3036) + FakeMessagePlatform (:9100) + CDP Chrome DevTools (:9222) -**登录:** admin@gochat.local / changeme (CDP 浏览器登录,click navigation) -**前置条件:** FakeMessagePlatform 已实现(Plan Task 1-13 全部完成),Round 4 的 28 页功能测试已通过。 - ---- - -## 1. Summary - -本轮 QA 通过 CDP Chrome DevTools 浏览器验证了 FakeMessagePlatform 与 GoChat 的全链路消息集成。所有核心消息流通过浏览器 UI 操作验证(非 curl),包括前端 UI 创建 Fake Inbox、客户消息在 dashboard 实时显示、客服回复通过 FakeProvider 发回 FakeMessagePlatform。 - -| 维度 | 结果 | 验证方式 | -|------|------|----------| -| 服务健康检查 | ✓ 3/3 通过 | curl /health | -| DB schema integrity | ✓ 6/6 端点返回 401(非 500) | curl | -| Fake Inbox 创建 | ✓ 通过前端 UI 表单创建 | CDP click navigation | -| 渠道选择器渲染 | ✓ "Fake 测试平台" 卡片可见且可点击 | CDP snapshot | -| Fake.vue 表单渲染 | ✓ 标题/描述/4个字段/提交按钮全部正确 | CDP snapshot | -| i18n 翻译 | ✓ zh_CN 翻译全部正确显示 | CDP snapshot | -| 连通性测试 | ✓ FakeMessagePlatform → GoChat webhook 双向通信 | curl + CDP | -| 入站消息 → 创建会话 | ✓ "测试客户A" 会话出现在 dashboard | CDP 浏览器可见 | -| 出站消息 → FakePlatform 接收 | ✓ 客服 Ctrl+Enter 回复 → /receive 收到 | CDP UI 操作 + FakePlatform API | -| 28 页前端回归 | ⚠ 跳过(时间限制,核心场景已验证) | — | -| WebSocket /cable | ✓ 后端日志无 401,presence 心跳正常 | 后端日志 | - ---- - -## 2. 服务启动验证 - -| 服务 | 端口 | 健康检查 | 状态 | -|------|------|----------|------| -| GoChat Backend | :3000 | `GET /health` → 200 `{"status":"ok"}` | ✓ | -| Frontend Vite | :3036 | `GET /` → 200 | ✓ | -| FakeMessagePlatform | :9100 | `GET /health` → 200 `{"status":"ok","service":"fake-message-platform"}` | ✓ | - ---- - -## 3. DB Schema Integrity Check - -| 端点 | 状态码 | 结论 | -|------|--------|------| -| conversations/unread_counts | 401 | ✓ (非 500) | -| notifications | 401 | ✓ | -| custom_attribute_definitions/ | 401 | ✓ | -| agent_bots | 401 | ✓ | -| custom_filters/?filter_type=conversation | 401 | ✓ | -| custom_filters/?filter_type=contact | 401 | ✓ | - ---- - -## 4. Fake Inbox 创建验证(CDP 浏览器 UI 操作) - -### 4.1 渠道选择器验证 - -通过 CDP 浏览器 click navigation 导航到收件箱创建页面: -1. Dashboard → sidebar 点击「设置」→ 点击「收件箱」→ 点击「添加收件箱」 -2. 渠道选择页面渲染 11 个渠道卡片,包括 **"Fake 测试平台"** 卡片 -3. 卡片描述:"创建用于自动化测试的 Fake 消息渠道" -4. 卡片可点击(非 disabled 状态) - -**发现并修复的问题:** `ChannelItem.vue` 的 `isActive` computed 中,`fake` 不在白名单数组里,导致卡片初始为 disabled。已修复:在 `isActive` 的 `return [...].includes(key)` 数组中添加 `'fake'`。 - -**影响文件:** `frontend/app/javascript/dashboard/components/widgets/ChannelItem.vue:59-70` - -### 4.2 Fake.vue 表单验证 - -点击 "Fake 测试平台" 卡片后,页面跳转到 `/settings/inboxes/new/fake`,渲染 Fake.vue 组件: -- 标题:"Fake 测试频道"(i18n: `FAKE_CHANNEL.TITLE`) -- 描述:"创建一个 Fake 消息渠道,用于自动化集成测试。"(i18n: `FAKE_CHANNEL.DESC`) -- 表单字段: - - 频道名称(必填,i18n: `FAKE_CHANNEL.CHANNEL_NAME`) - - 标识符(必填,i18n: `FAKE_CHANNEL.IDENTIFIER`) - - Webhook URL(带说明文字,i18n: `FAKE_CHANNEL.WEBHOOK_URL`) - - Token(可选,带说明文字,i18n: `FAKE_CHANNEL.TOKEN`) -- 提交按钮:"创建 Fake 频道"(i18n: `FAKE_CHANNEL.SUBMIT_BUTTON`) - -### 4.3 表单提交 - -填写表单: -- 频道名称:`Fake Test Inbox` -- 标识符:`fake_test_1` -- Webhook URL:`http://127.0.0.1:9100/receive` -- Token:`fake_test_token` - -点击「创建 Fake 频道」→ 页面跳转到 `/settings/inboxes/new/3/agents`(inbox_id=3),表单提交成功。 - -### 4.4 Agent 分配 - -在 agent 分配页面点击「添加客服代理」按钮。通过 API 补充添加 admin 为 inbox 成员(前端 UI 的添加操作未正确写入 inbox_members,通过 `POST /api/v1/accounts/1/inboxes/3/members` 补充)。 - ---- - -## 5. FakeMessagePlatform 全链路测试结果(CDP 浏览器验证) - -### 5.1 客户发消息 → GoChat 创建会话 → 前端 dashboard 显示 - -通过 FakeMessagePlatform 发送消息: -``` -POST http://127.0.0.1:9100/api/send -{"inbox_identifier":"fake_test_1","sender_id":"customer_001","sender_name":"测试客户A","content":"你好,我需要帮助"} -``` - -**CDP 浏览器验证结果:** -- Dashboard 「所有的」标签下出现会话 "测试客户A" -- 会话消息预览显示 "你好,我需要帮助" -- 点击会话后,聊天窗口显示完整消息 "你好,我需要帮助",时间 "Jul 9, 5:28 PM" -- 回复框可见,带 "发送 (CTRL + ↵)" 按钮 - -✓ **入站消息流通过浏览器 UI 验证** - -### 5.2 客服回复 → FakeMessagePlatform 收到出站消息 - -在 CDP 浏览器中操作: -1. 点击回复框(contenteditable div) -2. 输入 "您好,有什么可以帮您?" -3. 按 Ctrl+Enter 发送 - -**验证结果:** -- 消息即时出现在聊天窗口 -- FakeMessagePlatform `/api/messages` 返回 received 数组包含: - - msg_id=112, content="您好,有什么可以帮您?", sender.type="user" - -✓ **出站消息流通过浏览器 UI 验证** - -### 5.3 多客户并发会话 - -通过 FakeMessagePlatform 发送 customer_002 ("退款咨询") 和 customer_003 ("技术支持"): -- 各自创建独立会话和联系人 -- DB 确认 6 个独立会话(inbox_id=3) - -✓ - -### 5.4 打字状态指示 - -- `POST /api/typing` typing=true → GoChat 收到 typing.start 事件 ✓ -- `POST /api/typing` typing=false → GoChat 收到 typing.stop 事件 ✓ - -### 5.5 聊天窗口关闭(session end) - -- `POST /api/close` → GoChat 创建 "[session ended]" 消息 ✓ - -### 5.6 消息附件 - -- 发送 content_type=image + attachments → Message 创建为 image 类型 ✓ - ---- - -## 6. WebSocket /cable 验证 - -- 后端日志无 `ws: authentication failed` -- ActionCable JS 库加载正常 -- WS presence heartbeat 正常运行(后端日志可见 `ws: message command from user=1, data={"action":"update_presence"}`) -- Dashboard 登录后正常渲染 - ---- - -## 7. 28 页功能回归测试 - -**状态:** 跳过 - -**原因:** 本轮 QA 重点是 FakeMessagePlatform 全链路验证(Phase 3),该目标已通过 CDP 浏览器验证达成。28 页回归测试应在后续单独执行。 - -已验证的页面(通过 click navigation): -- Login → Dashboard ✓ -- Dashboard → Settings → Inboxes ✓ -- Inboxes → Add Inbox → Channel List ✓ -- Channel List → Fake.vue 表单 ✓ -- Fake.vue 表单提交 → Agent 分配页 ✓ -- Dashboard → 会话列表 → 会话详情 ✓ -- 会话详情 → 输入回复 → Ctrl+Enter 发送 ✓ - ---- - -## 8. Findings - -### BUG-D (P2) — ChannelItem.vue isActive 白名单未包含 fake - -- **Severity:** P2 -- **Symptom:** Fake 渠道卡片在选择页面显示为 disabled,无法点击 -- **Root cause:** `ChannelItem.vue` 的 `isActive` computed 中,最终的 `return [...].includes(key)` 白名单数组不包含 `'fake'` -- **Fix:** 在白名单数组中添加 `'fake'` -- **文件:** `frontend/app/javascript/dashboard/components/widgets/ChannelItem.vue:59-70` -- **状态:** FIXED(本轮修复) - -### BUG-E (P3) — 前端 agent 分配页面未正确写入 inbox_members - -- **Severity:** P3 -- **Symptom:** 在 agent 分配页面点击「添加客服代理」后,inbox_members 表中无记录,导致 admin 无法看到 fake inbox 的会话 -- **Workaround:** 通过 API `POST /api/v1/accounts/1/inboxes/3/members` 手动添加 -- **状态:** 未修复(非 fake 渠道特有问题,是 inbox member 前端流程的通用问题) - ---- - -## 9. Known Issues 回归验证 - -| Bug | Round 4 状态 | Round 5 状态 | -|-----|-------------|-------------| -| BUG-1 (P2) Activity 路由 | FIXED | 未测试 | -| BUG-2 (P3) Bots sidebar 导航 | FIXED | 未测试 | -| BUG-3 (P4) intlify empty key | PERSISTING | 未测试 | -| BUG-A (P3) Bot Reports 侧栏导航 | NEW | 未测试 | -| BUG-C (P2) 预聊天表单 null | FIXED | 未测试 | - ---- - -## 10. 后端错误观察 - -| 类别 | 观察 | -|------|------| -| 500s | 无 | -| Panics | 无 | -| WS auth failures | 无 | -| Meilisearch | 未运行 (port 7700 connection refused),后台 job 累积为 dead 状态。非阻断性。 | - ---- - -## 11. 测试备注 - -**测试了什么(CDP 浏览器验证):** -- 前端 UI 创建 Fake Inbox:渠道选择器 → Fake.vue 表单 → 提交 → agent 分配 -- 入站消息:FakeMessagePlatform 发消息 → GoChat 创建会话 → dashboard "所有的" 标签显示会话 "测试客户A" + 消息 "你好,我需要帮助" -- 出站消息:客服在聊天窗口输入 "您好,有什么可以帮您?" → Ctrl+Enter → FakeMessagePlatform /receive 收到消息 -- Console hooks 注入:登录后 console 干净(0 errors / 0 warnings) - -**测试了什么(API + DB 验证):** -- 多客户并发、打字状态、会话关闭、附件(通过 curl + psql 验证 DB 持久化) -- 连通性测试、FakeMessagePlatform REST API - -**跳过了什么:** -- 28 页前端 click-navigation 回归矩阵 -- 跨标签页实时推送测试 -- 直接 WS welcome+ping 测试 -- 多客服轮询分配测试(Phase 4) - -**修复的 bug:** -- BUG-D: ChannelItem.vue isActive 白名单未包含 fake(P2,已修复) - ---- - -## 12. Go 后端/TS 单元测试结果 - -| 测试套件 | 测试数 | 通过 | 失败 | -|----------|--------|------|------| -| Go fake_test.go (ChannelProvider) | 14 | 14 | 0 | -| TS integration.test.ts (FakeMessagePlatform) | 10 | 10 | 0 | -| Go router_test.go (route registration) | 1 | 1 | 0 | -| Go build ./... | — | ✓ | — | -| Go vet ./... | — | ✓ | — | diff --git a/docs/qa/2026-07-09-test-plan-round5.md b/docs/qa/2026-07-09-test-plan-round5.md deleted file mode 100644 index 43fd2950..00000000 --- a/docs/qa/2026-07-09-test-plan-round5.md +++ /dev/null @@ -1,435 +0,0 @@ -# Test Plan — CDP 自动化全链路测试 (Round 5) - -**日期:** 2026-07-09 -**环境:** backend (:3000) + frontend Vite (:3036) + FakeMessagePlatform (:9100) + CDP browser (:9222) -**登录:** admin@gochat.local / changeme -**前置条件:** FakeMessagePlatform 已实现(Plan 文档 Task 1-13 全部完成),Round 4 的 28 页功能测试已通过,WebSocket 认证修复已验证。 - ---- - -## 背景 - -Round 4 验证了 28 页功能页面 + WebSocket 认证修复 + 实时消息推送。但以下场景因未对接消息收发平台而未被实际验证: - -- 外部渠道 → GoChat:客户通过外部平台发消息,webhook 接收并创建会话 -- GoChat → 外部渠道:客服回复后消息通过 provider 发回外部平台 -- 多客服同时在线的消息分配 -- 客服上下线状态切换对消息路由的影响 -- 聊天窗口关闭/重开的对话连续性 -- 打字状态指示跨渠道传递 - -本轮引入 FakeMessagePlatform 作为可编程的假消息平台,覆盖上述全部场景。同时保留 Round 4 的 28 页回归测试。 - ---- - -## 测试规则(继承 Round 4,无放松) - -### Rule 1: Console warnings ARE errors -- `console.warn` / Vue warnings / i18n fallback / deprecation notices 全部记录为 finding -- 允许的 pre-existing warnings(不变):onClose deprecated、WootInput deprecated、Lit dev mode、Vue Router "Discarded invalid param(s)"、SW registration SecurityError -- 任何不在 allowlist 中的新 warning IS a bug - -### Rule 2: Network infinite request loop detection -- 每页加载并 settle 后,监控网络 10 秒 -- 标记任何在无用户交互下被调用超过 3 次的端点 -- 特别关注 /cable 重连循环、轮询模式、4xx/5xx 端点重试 -- `cache_keys` 端点多次调用是 false-positive(各组件独立获取,settle 后停止) - -### Rule 3: Click-only navigation — NO URL input -- 所有页面切换通过 UI 点击(sidebar links、sub-menus、tabs、buttons) -- 唯一例外:初始登录页 `/app/login` -- 折叠的 Settings sub-links 用 JS `.click()` on `` 元素(算 UI click) -- `window.location.href = '...'` 禁止用于页面导航 - -### Rule 4: Transition error capture -- 离开页面前 capture console buffer,到达下一页后 capture -- 过渡期间出现的 error/warning 是 transition-error finding - -### Rule 5: CRUD interaction where applicable -- 不只是"页面加载"——在支持 CRUD 的页面至少执行一次 create/edit 操作 -- 验证操作成功 + 无新 console errors + 无 infinite refetch loop - ---- - -## Pre-Test Setup - -### Step 0a: 启动全部服务 - -```bash -# Terminal 1: GoChat 后端 (:3000) -cd /home/yanghao05/Projects/gochat && pnpm dev:backend - -# Terminal 2: Frontend Vite (:3036) -cd /home/yanghao05/Projects/gochat && pnpm dev:frontend - -# Terminal 3: FakeMessagePlatform (:9100) -cd /home/yanghao05/Projects/gochat && pnpm fake:start -``` - -- [ ] `curl http://127.0.0.1:3000/health` → 200 -- [ ] `curl http://127.0.0.1:3036/` → 200 -- [ ] `curl http://127.0.0.1:9100/health` → `{"status":"ok","service":"fake-message-platform"}` - -### Step 0b: DB schema integrity check(Round 2 BUG-7 教训) - -```bash -for ep in conversations/unread_counts notifications custom_attribute_definitions/ agent_bots "custom_filters/?filter_type=conversation" "custom_filters/?filter_type=contact"; do - code=$(curl -s -o /dev/null -w '%{http_code}' -H 'X-Account-Id: 1' "http://127.0.0.1:3000/api/v1/accounts/1/$ep") - echo "$ep → $code" -done -``` - -全部返回 200(或 401 未认证),不可 500。任何 500 = 停下修 schema。 - -### Step 0c: CDP browser + console hooks - -- [ ] Chrome :9222 alive — `curl http://127.0.0.1:9222/json/version` -- [ ] 打开 `http://127.0.0.1:3036/app/login` -- [ ] 注入 console hooks(`window.__consoleErrors` / `__consoleWarnings` / `__resetConsole`) -- [ ] 验证返回 `'ok'` - -### Step 0d: Login - -- [ ] 填写 admin@gochat.local / changeme,点击登录 -- [ ] 验证重定向到 dashboard -- [ ] Capture console — post-login 干净 -- [ ] 验证 cookie `cw_d_session_info` 包含 `access-token` - -### Step 0e: 创建 Fake 渠道 Inbox - -通过前端 UI 创建(验证 Task 7-9 的前端兼容性): - -- [ ] Sidebar → 设置 → 收件箱 → 点击"创建新收件箱" -- [ ] 渠道选择列表中找到"Fake 测试平台"卡片,点击 -- [ ] 填写表单: - - 频道名称:`Fake Test Inbox` - - Identifier:`fake_test_1` - - Webhook URL:`http://127.0.0.1:9100/receive` - - Token:`fake_test_token` -- [ ] 点击"创建 Fake 频道" -- [ ] 验证跳转到 agent 分配页面 -- [ ] 添加 admin 到此 inbox -- [ ] 验证 inbox 出现在收件箱列表中 - -通过 API 验证: -```bash -curl -s -H "X-Account-Id: 1" -H "Authorization: Bearer " \ - http://127.0.0.1:3000/api/v1/accounts/1/inboxes | jq '.[] | select(.channel_type=="fake")' -``` - -### Step 0f: 验证 FakeMessagePlatform ↔ GoChat 连通性 - -```bash -# 从 FakeMessagePlatform 发一条测试消息到 GoChat -curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"smoke_test","sender_name":"连通性测试","content":"ping"}' - -# 预期:GoChat 收到 webhook,创建 contact + conversation + message -# 验证 GoChat 日志中出现 "Fake webhook received" + "message persisted" - -# 验证 FakeMessagePlatform 状态 -curl http://127.0.0.1:9100/api/status -# 预期:sent >= 1 -``` - -如果连通性测试失败,停下排查——后续所有测试依赖这条链路。 - ---- - -## Phase 1: WebSocket /cable 验证(回归 Round 4) - -- [ ] 登录后等 5s,检查后端日志 `ws: connection established` -- [ ] 后端日志无 `ws: authentication failed` -- [ ] 浏览器 console 无 WebSocket errors、无 /cable 401 -- [ ] 网络 15s:/cable 连接一次并保持,不重复重连 -- [ ] Presence indicator 显示状态(online/busy/offline) -- [ ] 直接 WS 测试:连 `ws://127.0.0.1:3036/cable?access-token=`,收到 `{"type":"welcome"}` + 5s ping - ---- - -## Phase 2: 28 页功能回归测试(click navigation) - -继承 Round 4 的 28 页矩阵。本轮重点验证回归——Round 4 发现的 bug 是否修复、是否回退。每页执行完整 per-page checklist(reset buffer → click → settle → console → network 10s → CRUD → backend logs → transition capture)。 - -| # | 页面 | 点击路径 | CRUD 测试 | 回归关注 | -|---|------|----------|-----------|----------| -| 1 | Dashboard | (post-login) | 验证会话列表 + 在线状态 | — | -| 2 | 会话详情 | 点击会话 | 发消息,验证即时送达 | — | -| 3 | 联系人 | Sidebar: 联系人 | 编辑联系人,保存 | — | -| 4 | 报告 Overview | Sidebar: 报告 → Overview | 日期选择器变化 | — | -| 5 | 报告 会话 | 报告 sub-tab: 会话 | 筛选变化 | — | -| 6 | 报告 客服 | 报告 sub-tab: 客服 | 验证表格 | — | -| 7 | 报告 标签 | 报告 sub-tab: 标签 | 验证加载 | — | -| 8 | 报告 收件箱 | 报告 sub-tab: 收件箱 | 验证加载 | — | -| 9 | 报告 团队 | 报告 sub-tab: 团队 | 验证加载 | — | -| 10 | 报告 CSAT | 报告 sub-tab: 客户满意度 | 验证加载 | — | -| 11 | 报告 SLA | 报告 sub-tab: SLA | 验证 SLA 指标 | — | -| 12 | 报告 机器人 | 报告 sub-tab: 机器人 | 验证加载 | **BUG-A** 回归:从 Reports 页面内点"机器人" sidebar link 是否跳转 | -| 13 | 活动 | Sidebar: 活动 | 验证页面加载 | **BUG-1** 回归:路由是否正常 | -| 14 | 帮助中心 | Sidebar: 帮助中心 | 点击 portal,验证文章 | — | -| 15 | 设置 账户设置 | Settings → 账户设置 | 更新字段,保存 | — | -| 16 | 设置 客服代理 | Settings sub: 客服代理 | 验证列表 + 搜索 | — | -| 17 | 设置 团队 | Settings sub: 团队 | 打开创建团队向导 | — | -| 18 | 设置 收件箱 | Settings sub: 收件箱 | 点击 inbox config,加载全部 tab | **BUG-C** 回归:预聊天表单 tab | -| 19 | 设置 标签 | Settings sub: 标签 | 创建标签 | **BUG-3** 回归:intlify empty key warning | -| 20 | 设置 自定义属性 | Settings sub: 自定义属性 | 切换 tab | — | -| 21 | 设置 自动化 | Settings sub: 自动化 | 验证列表 | — | -| 22 | 设置 机器人 | Settings sub: 机器人 | 验证列表 | **BUG-2** 回归:sidebar click nav | -| 23 | 设置 宏 | Settings sub: 宏 | 验证列表 | — | -| 24 | 设置 预设回复 | Settings sub: 预设回复 | 创建预设回复 | — | -| 25 | 设置 集成方式 | Settings sub: 集成方式 | 点击配置一个 | — | -| 26 | 设置 会话工作流 | Settings sub: 会话工作流 | Toggle switch | — | -| 27 | 设置 客服分配 | Settings sub: 客服分配 | 验证表单 | — | -| 28 | 个人资料 | Avatar dropdown → profile | 更新资料,保存 | — | - -### 回归判定标准 - -| Bug | Round 4 状态 | Round 5 判定 | -|-----|-------------|-------------| -| BUG-1 (P2) Activity 路由缺失 | FIXED | 点击"活动" → campaigns 页面加载 → FIXED | -| BUG-2 (P3) Bots sidebar 导航 | FIXED | Settings → 机器人 → 导航到 agent-bots → FIXED | -| BUG-3 (P4) intlify empty key | PERSISTING | 创建标签时无 `[intlify] Not found ''` → FIXED;仍有 → PERSISTING | -| BUG-A (P3) Bot Reports 侧栏导航 | NEW | 从 Reports 页面点"机器人" → 跳转到 /reports/bot → FIXED;不跳转 → PERSISTING | -| BUG-C (P2) 预聊天表单 null | FIXED | inbox settings → 预聊天表单 tab → 无 Vue warn → FIXED | - ---- - -## Phase 3: FakeMessagePlatform 全链路消息测试(本轮新增核心) - -这是 Round 5 的核心差异——通过 FakeMessagePlatform 驱动真实的外部渠道消息流。 - -### 3.1 客户发消息 → GoChat 创建会话 - -- [ ] 通过 FakeMessagePlatform 发送消息: - ```bash - curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","sender_name":"测试客户A","content":"你好,我需要帮助"}' - ``` -- [ ] 前端 Dashboard 看到新会话出现(Fake Test Inbox 渠道) -- [ ] 会话列表显示客户名"测试客户A"和消息预览"你好,我需要帮助" -- [ ] 点击会话,消息出现在聊天窗口 -- [ ] 后端日志:`Fake webhook received` + `message persisted` -- [ ] Console 无新错误 - -### 3.2 客服回复 → FakeMessagePlatform 收到出站消息 - -- [ ] 在前端会话详情页,客服输入回复"您好,有什么可以帮您?",Ctrl+Enter 发送 -- [ ] 消息即时出现在聊天窗口(WS 实时推送) -- [ ] FakeMessagePlatform 收到出站消息: - ```bash - curl http://127.0.0.1:9100/api/messages?inbox_identifier=fake_test_1 - ``` - 预期:返回包含 `"content":"您好,有什么可以帮您?"` 的消息 -- [ ] 消息 sender.type 为 "agent" -- [ ] Console 无新错误 - -### 3.3 客户回复 → 消息追加到同一会话 - -- [ ] 通过 FakeMessagePlatform 回复: - ```bash - curl -X POST http://127.0.0.1:9100/api/reply \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","content":"我的订单有问题","reply_to_id":""}' - ``` -- [ ] 前端会话详情页:新消息追加到聊天窗口底部(不创建新会话) -- [ ] 消息 sender 为客户"测试客户A" -- [ ] Console 无新错误 - -### 3.4 多客户并发会话 - -- [ ] 同时发送两个不同客户的消息: - ```bash - curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_002","sender_name":"测试客户B","content":"退款咨询"}' & - - curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_003","sender_name":"测试客户C","content":"技术支持"}' & - ``` -- [ ] 前端会话列表出现 3 个独立会话(A/B/C) -- [ ] 每个会话的消息内容正确对应 -- [ ] Console 无新错误 - -### 3.5 打字状态指示 - -- [ ] 触发客户打字状态: - ```bash - curl -X POST http://127.0.0.1:9100/api/typing \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","typing":true}' - ``` -- [ ] 前端会话详情页显示"正在输入..."指示器(如果 UI 支持) -- [ ] 停止打字: - ```bash - curl -X POST http://127.0.0.1:9100/api/typing \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","typing":false}' - ``` -- [ ] 指示器消失 -- [ ] Console 无新错误 - -### 3.6 聊天窗口关闭(session end) - -- [ ] 关闭客户会话: - ```bash - curl -X POST http://127.0.0.1:9100/api/close \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","conversation_id":"","sender_id":"customer_001"}' - ``` -- [ ] GoChat 收到 session.end 事件,创建 "[session ended]" 消息 -- [ ] 前端会话显示系统消息或状态变化 -- [ ] Console 无新错误 -- [ ] 同一客户再次发消息 → 创建新会话(如果 inbox 配置 lock_to_single_conversation=false) - -### 3.7 消息附件 - -- [ ] 发送带附件的消息: - ```bash - curl -X POST http://127.0.0.1:9100/api/send \ - -H 'Content-Type: application/json' \ - -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_001","sender_name":"测试客户A","content":"请看这张截图","content_type":"image","attachments":[{"url":"http://example.com/screenshot.png","content_type":"image/png","filename":"screenshot.png","file_size":102400}]}' - ``` -- [ ] 前端会话详情页显示附件预览(图片缩略图或文件链接) -- [ ] Console 无新错误 - ---- - -## Phase 4: 多客服消息分配测试(本轮新增) - -### 4.1 单客服在线 — 所有消息分配给该客服 - -- [ ] 确保 Fake Test Inbox 启用 auto_assignment -- [ ] 确保只有一个客服(admin)在线 -- [ ] FakeMessagePlatform 模拟客服上线: - ```bash - curl -X POST http://127.0.0.1:9100/api/agent/online \ - -H 'Content-Type: application/json' \ - -d '{"agent_id":"1","agent_name":"Admin"}' - ``` -- [ ] 发送新客户消息 → 验证会话自动分配给 admin -- [ ] 前端会话列表中该会话 assignee 为 admin - -### 4.2 多客服在线 — 轮询分配 - -- [ ] 创建第二个客服用户(通过 Settings → 客服代理) -- [ ] 将第二个客服添加到 Fake Test Inbox -- [ ] 两个客服同时在线 -- [ ] 连续发送 3 条不同客户的消息 -- [ ] 验证会话按 round-robin 或 least_busy 策略分配给不同客服 -- [ ] 前端各客服的会话列表显示分配给自己的会话 - -### 4.3 客服下线 — 消息不分配给离线客服 - -- [ ] 第二个客服下线: - ```bash - curl -X POST http://127.0.0.1:9100/api/agent/offline \ - -H 'Content-Type: application/json' \ - -d '{"agent_id":"2"}' - ``` -- [ ] 发送新客户消息 → 验证会话只分配给在线的 admin -- [ ] 不分配给已下线的客服 - -### 4.4 全部客服下线 — 消息进入未分配队列 - -- [ ] admin 也下线 -- [ ] 发送新客户消息 → 验证会话创建但 assignee 为空(未分配) -- [ ] 客服上线后 → 验证是否自动补分配(取决于 assignment policy 配置) - ---- - -## Phase 5: 实时 WebSocket 功能验证(含跨渠道) - -### 5.1 基础实时推送(回归 Round 4) - -- [ ] 打开会话,发消息 → 即时出现 -- [ ] 后端日志:`message.created` event dispatched -- [ ] Presence indicator 显示正确状态 -- [ ] 网络 30s:/cable 稳定,无 401,无重连风暴 -- [ ] 直接 WS 测试:welcome + 5s ping - -### 5.2 跨标签页实时推送(回归 Round 4) - -- [ ] Tab 2 同账号登录,reload dashboard -- [ ] Tab 1 发消息 → Tab 2 会话列表预览更新 + 会话详情消息出现 -- [ ] 无需手动刷新 - -### 5.3 FakeMessagePlatform 触发的实时推送(新增) - -- [ ] Tab 1 打开 customer_001 的会话 -- [ ] 通过 FakeMessagePlatform 发送 customer_001 的新消息 -- [ ] Tab 1 聊天窗口即时显示新消息(WS 推送,非轮询) -- [ ] Tab 2 如果也打开同一会话 → 同样即时显示 -- [ ] 后端日志:`Fake webhook received` → `message persisted` → `ws: message command`(WS 推送) - ---- - -## Phase 6: 收集与报告 - -### Issue severity(继承 Round 4) - -| Sev | 定义 | -|-----|------| -| P0 | 功能损坏,阻断核心工作流,或无限循环导致资源耗尽 | -| P1 | 核心功能损坏但有 workaround;或重复 console errors 降低 UX | -| P2 | 非核心功能损坏,或 warnings 指向真实代码问题 | -| P3 | 轻微 cosmetic/edge-case,无功能影响 | -| P4 | 纯噪声(dev-mode warnings、deprecation 无用户影响) | - -### 报告文件 - -写入 `docs/qa/2026-07-09-qa-report-round5.md`。 - -必须包含的章节: -1. **Summary** — 总页数/通过/失败数 + FakeMessagePlatform 集成测试结果概要 -2. **服务启动验证** — backend/frontend/fake-platform 三个服务的健康检查结果 -3. **Fake Inbox 创建验证** — 前端 UI 创建 fake 渠道 inbox 的过程和结果 -4. **WebSocket 验证** — /cable 状态、welcome/ping、无 401 -5. **28 页回归测试结果表** — 页面、点击路径、通过/失败、findings -6. **FakeMessagePlatform 全链路测试结果**(Phase 3 每个子项) -7. **多客服消息分配测试结果**(Phase 4 每个子项) -8. **实时 WebSocket 功能验证**(Phase 5 每个子项) -9. **Findings** — 每个 issue:severity、复现步骤(click path + curl command)、预期 vs 实际、console errors、screenshot path、根因方向、影响文件 -10. **Known issues 回归验证** — BUG-1/2/3/A/C 的 Round 5 状态 -11. **后端错误观察** — 500s、panics、WS auth failures -12. **测试备注** — 测试了什么、跳过了什么、blockers - -### 截图 - -每个 finding 截图: -``` -browser_vision(question="Capture the issue: ", annotate=false) -``` -在 CLI 模式下陈述截图的绝对路径(不使用 MEDIA: tag)。 - -### 文档清理 - -按 known-bugs.md 的 cleanup policy: -- [ ] 将 Round 4 的 `docs/qa/2026-07-09-qa-report-round4.md` 和 `docs/qa/2026-07-09-test-plan-round4.md` 删除 -- [ ] 将 Round 5 报告中的新发现合并到 `references/known-bugs.md` -- [ ] 更新 known-bugs.md 的 "Consolidated from QA rounds 1–5" 行 -- [ ] 保留 Round 5 的报告和测试计划作为当前版本 - ---- - -## 验证 Checklist - -- [ ] 三个服务(backend :3000 + frontend :3036 + fake :9100)全部启动并健康 -- [ ] DB schema integrity check:6 个历史端点全部 200(非 500) -- [ ] Console hooks 注入成功 -- [ ] Login 成功,cookie 包含 access-token -- [ ] Fake 渠道 Inbox 通过前端 UI 成功创建 -- [ ] FakeMessagePlatform → GoChat 连通性测试通过 -- [ ] /cable WebSocket 连接稳定(welcome + ping + 无 401) -- [ ] 28 页全部通过 click navigation 测试(或标注 exception) -- [ ] 每页:reset buffer → navigate → settle → console → network 10s → CRUD → logs → transition -- [ ] Phase 3 全链路消息测试:发消息/回复/多客户/打字/关窗口/附件 全部验证 -- [ ] Phase 4 多客服分配:单客服/多客服轮询/下线/全下线 全部验证 -- [ ] Phase 5 实时推送:基础/跨标签页/FakePlatform 触发 全部验证 -- [ ] 报告写入 `docs/qa/2026-07-09-qa-report-round5.md` -- [ ] 每个 finding 有:severity、复现步骤(click path + curl)、console errors、screenshot path、根因 -- [ ] Known bugs 回归验证:BUG-1/2/3/A/C 状态更新 -- [ ] 旧轮报告清理(Round 4 report + test plan 删除) -- [ ] known-bugs.md 更新(新发现 + 状态变更 + "rounds 1–5") diff --git a/docs/qa/2026-07-10-test-plan-round7.md b/docs/qa/2026-07-10-test-plan-round7.md new file mode 100644 index 00000000..04cb0506 --- /dev/null +++ b/docs/qa/2026-07-10-test-plan-round7.md @@ -0,0 +1,304 @@ +# QA Test Plan — CDP Full-Page Functional Testing Round 7 + +> **Date:** 2026-07-10 +> **Commit tested:** 690796a (fix: resolve 4 bugs from QA round 6 — BUG-F/G/H/I) +> **Previous round:** Round 6 (Redis Stream migration verification, 11 pages, 4 findings) +> **Round 7 focus:** BUG-F/G/H/I fix verification + full 28-page matrix completion + FakeMessagePlatform E2E + +--- + +## 1. Round 7 Goals + +### 1.1 Primary: Verify 4 Bug Fixes from Round 6 + +| Bug | Sev | Fix Summary | Verification Method | +|-----|-----|-------------|---------------------| +| BUG-F | P1 | `fake_webhook.go:168` now casts `channel_config::jsonb` before `@>` operator | Send fake webhook → verify message persisted in DB + appears in browser conversation list | +| BUG-G | P3 | `Settings.vue:544` passes `window.history.state` (not `null`) to `replaceState`; same fix in Instagram.vue, Tiktok.vue, portalThemeHelper.js | Navigate to Activity page → check console for Vue Router history.state warning (should be absent) | +| BUG-H | P3 | `account_service.go` CacheKeys() now derives real inbox/label/team keys from DB state (count + max(updated_at)) instead of hardcoded `"0000000000"` | Navigate to Settings → Inboxes → verify both inboxes (web_widget + fake) appear in list | +| BUG-I | P3 | `worker.go:386` generates unique `consumerID := fmt.Sprintf("%s-%d", wp.workerID, index)` per goroutine | Redis `XINFO CONSUMERS` for any queue → verify 5 consumers (not 1) | + +### 1.2 Secondary: Complete the 28-Page Matrix + +Round 6 tested only 11 pages (browser session timeout). Round 7 must cover ALL 28 pages +in the standard matrix, with special attention to pages skipped in round 6: + +- Settings: Teams, Labels, Custom Attributes, Automation, Macros, Canned Responses, + Integrations, Conversation Workflow +- Profile page +- Reports: Conversations, Agents, Labels, Inboxes, Teams, CSAT (round 6 only tested + Overview, SLA, Bot) + +### 1.3 Tertiary: FakeMessagePlatform E2E Integration + +BUG-F blocked all fake channel testing in round 6. With the fix, perform the full +inbound → outbound message flow: + +1. Start FakeMessagePlatform (:9100) +2. Send customer message via `POST /api/send` → GoChat webhook → DB persist +3. Verify in browser: conversation appears in dashboard, message renders in chat +4. Agent reply in browser (Ctrl+Enter) → FakeMessagePlatform `/receive` → verify via + `GET /api/messages` +5. Verify real-time delivery: message appears instantly (WebSocket) + +### 1.4 Quaternary: Complete Phase 5 Real-time Verification + +Round 6 skipped Phase 5 (iteration limit). Round 7 must: + +- Send message → instant render in chat UI +- Verify `message.created` event in backend logs +- Presence indicator shows status (online/busy/offline) +- 30s stable /cable monitoring +- Direct WS test via shell (websocat or Python) +- Cross-tab real-time delivery (2 tabs, same credentials) + +### 1.5 Quinary: Redis Worker Observability (BUG-I fix) + +- Start backend with `concurrency: 5` (config.dev.yaml) +- Trigger at least one job (send a message) +- Redis `XINFO CONSUMERS gochat:jobs:high gochat-workers` → verify 5 consumers +- Verify each consumer has a unique name (`worker--0` through `-4`) + +--- + +## 2. Test Execution Plan + +### Phase 1 — Start Dev Services + Pre-checks + +1. Start backend (air hot-reload, :3000) and frontend (Vite, :3036) as background processes +2. Health check: `curl :3000/health` and `curl :3036/` → both OK +3. DB schema integrity check: hit 6 historically-missing endpoints → all 200 (not 500) +4. Capture backend log baseline +5. Start FakeMessagePlatform: `cd channels/fake && npx tsx src/index.ts` (background) +6. Verify FakeMessagePlatform health: `curl :9100/health` + +### Phase 2 — Browser Login + Console Hook Injection + +1. Navigate to `http://127.0.0.1:3036/app/login` (only allowed direct URL) +2. Inject console hooks (error + warn capture, `__resetConsole`) +3. Fill login form: `admin@gochat.local` / `changeme` +4. Submit → verify redirect to `/app/` (dashboard) +5. Re-inject hooks if full-page reload occurred +6. Read console buffer → verify clean + +### Phase 3 — WebSocket /cable Verification + +1. Wait 3–5s for WS connect +2. Read console buffer for `/cable` 401 or WS errors +3. Poll backend logs for `ws: connection established` +4. Monitor network 10s — `/cable` stable, no reconnect loop +5. If /cable broken → record P0, continue page tests (note degraded real-time) + +### Phase 4 — Full 28-Page Matrix (Click Navigation) + +**Per-page protocol (EVERY page, non-negotiable):** + +1. `window.__resetConsole()` — reset buffer +2. Click to navigate (record click path) +3. Wait to settle — `browser_vision` confirms content rendered +4. Read console buffer → any error or non-allowlisted warning is a finding +5. Monitor network 10s → flag any endpoint called >3x (except `cache_keys`) +6. Verify core content renders (not blank, not error boundary) +7. Attempt one CRUD interaction where applicable +8. Poll backend logs for errors during this page +9. Capture transition errors before leaving + +**Page matrix:** + +| # | Page | Click Path | Key Checks | Round 6 Status | +|---|------|------------|------------|----------------| +| 1 | Dashboard | (post-login) | Conversation list, sidebar, online status | ✅ PASS | +| 2 | Conversation detail | Click a conversation | Messages, reply box, send msg, real-time | ✅ PASS | +| 3 | Contacts | Sidebar: 联系人 | Edit, save, search; no refetch loop | ✅ PASS | +| 4 | Reports Overview | Sidebar: 报告 → Overview | Stats cards, charts, date picker | ✅ PASS | +| 5 | Reports Conversations | Reports sub-tab: 会话 | Table, filter change | ⬜ NOT TESTED | +| 6 | Reports Agents | Reports sub-tab: 客服 | Agent table | ⬜ NOT TESTED | +| 7 | Reports Labels | Reports sub-tab: 标签 | Verify load | ⬜ NOT TESTED | +| 8 | Reports Inboxes | Reports sub-tab: 收件箱 | Verify load | ⬜ NOT TESTED | +| 9 | Reports Teams | Reports sub-tab: 团队 | Verify load | ⬜ NOT TESTED | +| 10 | Reports CSAT | Reports sub-tab: 客户满意度 | Verify load | ⬜ NOT TESTED | +| 11 | Reports SLA | Reports sub-tab: SLA | SLA metrics | ✅ PASS | +| 12 | Reports Bot | Reports sub-tab: 机器人 | BUG-A regression | ✅ PASS (FIXED) | +| 13 | Activity | Sidebar: 活动 (JS click) | BUG-1 + **BUG-G regression** | ✅ PASS (BUG-G warning) | +| 14 | Help Center | Sidebar: 帮助中心 (JS click) | Portal list, articles, tabs | ✅ PASS | +| 15 | Settings General | 设置 → 账户设置 | Update a field, save | ✅ PASS | +| 16 | Settings Agents | 设置: 客服代理 | List, search | ⬜ NOT TESTED | +| 17 | Settings Teams | 设置: 团队 | List, create team wizard | ⬜ NOT TESTED | +| 18 | Settings Inboxes | 设置: 收件箱 | **BUG-H regression: 2 inboxes visible** | ✅ PASS (BUG-H: 1 of 2) | +| 19 | Settings Labels | 设置: 标签 | Create label, **BUG-3/B regression** | ⬜ NOT TESTED | +| 20 | Settings Custom Attributes | 设置: 自定义属性 | List, tabs | ⬜ NOT TESTED | +| 21 | Settings Automation | 设置: 自动化 | List | ⬜ NOT TESTED | +| 22 | Settings Agent Bots | 设置: 机器人 | BUG-2 regression | ✅ PASS (FIXED) | +| 23 | Settings Macros | 设置: 宏 | List | ⬜ NOT TESTED | +| 24 | Settings Canned Responses | 设置: 预设回复 | Create canned response | ⬜ NOT TESTED | +| 25 | Settings Integrations | 设置: 集成方式 | Cards, configure one | ⬜ NOT TESTED | +| 26 | Settings Conv. Workflow | 设置: Conversation Workflows | Toggle switch | ⬜ NOT TESTED | +| 27 | Settings Assignment | 设置: Agent Assignment | Forms, add buttons | ⬜ NOT TESTED | +| 28 | Profile | Avatar dropdown → profile | Update profile, save | ⬜ NOT TESTED | + +**Inbox Settings Deep Dive (page 18 sub-tabs):** + +| Sub-tab | Key Check | Round 6 | +|---------|-----------|---------| +| 收件箱设置 | Config form loads | ✅ | +| 协作 | Agent assignment list | ⬜ | +| 预聊天表单 | BUG-C regression (null pre_chat_form_options) | ✅ FIXED | +| 后聊天表单 | Form renders | ⬜ | +| 消息标签 | Message labels config | ⬜ | +| 自动化规则 | Auto-assign rules | ⬜ | +| 业务时间 | Business hours config | ⬜ | + +### Phase 4b — BUG-F Verification: FakeMessagePlatform E2E + +**This is the highest-priority test in round 7 (P1 bug fix verification).** + +1. Navigate to Settings → Inboxes → verify Fake Test Inbox (id=3) is visible (BUG-H fix) +2. Record the Fake Test Inbox identifier (e.g., `fake_test_1`) +3. Configure FakeMessagePlatform webhook URL to match: + ``` + curl -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_test_1","token":"fake_test_token"}' + ``` +4. **Inbound test:** Send customer message via FakeMessagePlatform: + ``` + curl -X POST http://127.0.0.1:9100/api/send \ + -H 'Content-Type: application/json' \ + -d '{"inbox_identifier":"fake_test_1","sender_id":"customer_round7","sender_name":"Round7测试客户","content":"你好,这是第7轮测试消息"}' + ``` +5. Verify GoChat response: `{"status":"success"}` (NOT `{"status":"ignored"}`) +6. Verify in DB: + ```sql + SELECT id, content, sender_type, inbox_id, conversation_id + FROM messages WHERE inbox_id=3 ORDER BY id DESC LIMIT 5; + ``` +7. **Browser verification:** Navigate to Dashboard → verify new conversation appears + in conversation list with "Round7测试客户" and message preview "你好,这是第7轮测试消息" +8. Click the conversation → verify message renders in chat window +9. **Outbound test:** In the browser, click reply box, type "您好,有什么可以帮您?", + Ctrl+Enter to send +10. Verify FakeMessagePlatform received the outbound message: + ``` + curl http://127.0.0.1:9100/api/messages?inbox_identifier=fake_test_1 + ``` +11. Verify the outbound message appears in the chat UI with timestamp "now" +12. **Real-time verification:** If another browser tab is open, verify the message + appears in that tab without manual refresh + +### Phase 4c — BUG-D Regression: Channel Creation Page + +1. Navigate to Settings → Inboxes → click "添加收件箱" (Add Inbox) +2. Verify the "Fake 测试平台" channel card is visible and NOT disabled (BUG-D) +3. Do NOT create a new inbox unless testing BUG-E (inbox_members) + +### Phase 5 — Real-time WebSocket Feature Verification + +1. Open a conversation, send a message → verify instant render with "now" timestamp +2. Poll backend logs for `ws: connection established` and `message.created` event +3. Verify presence/availability indicator shows status (not empty) +4. Monitor network 30s — `/cable` stable, no 401s, no reconnect storm +5. **Direct WS test:** Extract JWT via shell curl → connect with websocat/Python → + expect `{"type":"welcome"}` then `{"type":"ping",...}` every 5s +6. **Cross-tab real-time delivery:** Open 2nd tab, login, send message from tab 1 → + verify it appears in tab 2 without refresh + +### Phase 6 — Redis Worker Observability (BUG-I Fix) + +1. Trigger a job: send an agent reply via API or browser +2. Redis CLI: + ``` + redis-cli XINFO CONSUMERS gochat:jobs:high gochat-workers + ``` +3. Verify 5 consumers with unique names: `worker--0` through `worker--4` +4. Verify at least 1 consumer has a pending=0 after ACK (job consumed successfully) + +### Phase 7 — Collect, Report, Teardown + +1. Kill background dev servers + FakeMessagePlatform +2. Write report to `docs/qa/2026-07-10-qa-report-round7.md` +3. Delete previous round's report from `docs/` (round 6 report at `docs/QA_REPORT_2026-07-10_cdp_round6.md`) +4. Update `references/known-bugs.md` with round 7 statuses + +--- + +## 3. Known Bug Re-Verification Matrix + +Every bug in the registry must be re-verified. Mark FIXED / PERSISTING / REGRESSED +with evidence. + +| Bug | Sev | Description | Round 6 Status | Round 7 Check | +|-----|-----|-------------|----------------|---------------| +| BUG-1 | P2 | Activity page route missing | FIXED | Sidebar "活动" → `/campaigns/live_chat` | +| BUG-2 | P3 | Bots sidebar link not navigating | FIXED | Settings → "机器人" → `/settings/agent-bots` | +| BUG-3/B | P4 | intlify empty key warnings | NOT TESTED | **Create a label** → check console | +| BUG-4 | P3 | Vue prop type mismatch on message ID | (assumed fixed) | Open conversation with messages → no prop warnings | +| BUG-5 | P1 | NotificationSetting handler not registered | (assumed fixed) | `curl notification_settings` → 200 | +| BUG-7 | P0 | Missing DB tables → 500 on 6 endpoints | FIXED | All 6 endpoints → 200 | +| BUG-A | P3 | Bot Reports sidebar nav from Reports | FIXED | Reports SLA → "机器人" → `/reports/bot` | +| BUG-C | P2 | Pre-chat Form tab null error | FIXED | Inbox settings → 预聊天表单 → no Vue error | +| BUG-D | P2 | ChannelItem isActive whitelist (fake) | NOT TESTED | Add Inbox page → Fake card NOT disabled | +| BUG-E | P3 | inbox_members not written | NOT TESTED | Create new inbox → verify inbox_members | +| BUG-F | P1 | Fake webhook JSONB @> on TEXT | NEW → **FIX PENDING VERIFY** | FakeMessagePlatform E2E (Phase 4b) | +| BUG-G | P3 | Vue Router history.state warning | NEW → **FIX PENDING VERIFY** | Activity page → console clean | +| BUG-H | P3 | Inbox list shows 1 of 2 | NEW → **FIX PENDING VERIFY** | Settings → Inboxes → 2 inboxes visible | +| BUG-I | P3 | Worker consumer name shared | NEW → **FIX PENDING VERIFY** | Redis XINFO → 5 consumers | + +--- + +## 4. Console Warning Allowlist + +Warnings in this list are acceptable and do NOT count as findings unless they +increase in frequency or appear in new contexts: + +| Warning | Where | Sev | +|---------|-------|-----| +| `[DEPRECATED] The 'onClose' prop is deprecated` | Widget/UI components | P4 | +| `[DEPRECATED] has be deprecated` | Old WootInput components | P4 | +| `Lit is in dev mode` / `Multiple versions of Lit loaded` | Initial load | P4 | +| `[Vue Router warn]: Discarded invalid param(s) "page"` | Route nav | P4 | +| `SW registration failed (SecurityError)` | Vite dev server | P4 | + +**BUG-G warning (`history.state seems to have been manually replaced`) should NOT +appear after the fix.** If it does, BUG-G is PERSISTING. + +--- + +## 5. Network False-Positive: `cache_keys` Endpoint + +`/api/v1/.../cache_keys` is called multiple times on page load. This is NOT an +infinite loop. Do not flag unless it continues repeating after the 10s settle window. + +**BUG-H context:** The fix changes `cache_keys` response values from hardcoded +`"0000000000"` to real DB-derived keys. The frontend uses these to invalidate +IndexedDB cache. After the fix, navigating to Settings → Inboxes should trigger +a fresh API call (not cached) and return both inboxes. + +--- + +## 6. Testing Principles (Non-Negotiable) + +1. **Console warnings ARE errors** — zero tolerance for non-allowlisted warnings +2. **Watch for infinite network loops** — 10s monitor per page, >3 calls = finding + (except `cache_keys`) +3. **Navigate by clicking, never by URL** — login is the sole exception +4. **Capture transition errors** — reset buffer before leaving, read after arriving +5. **CRUD interaction where applicable** — not just "page loads" +6. **Use `chrome_devtools` MCP if `browser_*` tools lose context 2+ times** (round 5 lesson) + +--- + +## 7. Success Criteria + +Round 7 is a PASS if: + +- ✅ All 28 pages tested via click navigation with per-page console/network checks +- ✅ BUG-F verified: FakeMessagePlatform E2E (inbound + outbound) works end-to-end +- ✅ BUG-G verified: Activity page has no `history.state` warning +- ✅ BUG-H verified: Settings → Inboxes shows both web_widget and fake inboxes +- ✅ BUG-I verified: Redis `XINFO CONSUMERS` shows 5 unique consumers +- ✅ WebSocket /cable connected and stable (30s, no reconnect storm) +- ✅ Direct WS test: welcome + ping frames received +- ✅ Cross-tab real-time delivery works +- ✅ All known bugs re-verified (FIXED / PERSISTING / REGRESSED status recorded) +- ✅ Zero new P0/P1 findings +- ✅ Report written to `docs/qa/2026-07-10-qa-report-round7.md`