Files
sino-mci/docs/testing/2026-07-07-test-plan.md

327 lines
15 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.

# sino-mci 测试补齐计划
> **日期**2026-07-07
> **范围**backendmci-server单元/集成测试、API 冒烟测试、前端手工测试、Chrome DevTools 测试场景、Docker 部署验证
> **执行标准命令**:见第 7 节
---
## 1. 单元测试(已存在)
| 测试类 | 路径 | 覆盖内容 |
|--------|------|---------|
| `PolicyEngineTest` | `send/service/PolicyEngineTest.java` | 频率限制rate_limit、时间窗time_window、黑名单blacklist及组合策略的允许/拒绝判定 |
| `SendServiceFailoverTest` | `send/service/SendServiceFailoverTest.java` | 发送记录主备账号切换、全部账号不可用、无健康账号三种场景下的 `SendService.dispatchRecord` |
| `AccountSelectorTest` | `channel/service/AccountSelectorTest.java` | 会话发送账号选择:主号健康、主号不健康切备用、`backup` 策略优先、无健康账号返回 null |
| `FlowEngineTest` | `inbound/engine/FlowEngineTest.java` | 入站流程引擎:条件分支命中 webhook、未命中执行 reply、无发布流程时标记已处理 |
| `IntentRecognitionServiceTest` | `inbound/intent/IntentRecognitionServiceTest.java` | 规则 + 槽位抽取的意图识别,无规则/无 LLM 时返回 `unknown` |
| `InboundContextServiceTest` | `inbound/service/InboundContextServiceTest.java` | 根据会话/联系人解析业务上下文,合并时优先保留会话维度字段 |
| `WebhookPayloadBuilderTest` | `inbound/webhook/WebhookPayloadBuilderTest.java` | 入站 webhook payload 结构、方向映射、`null` 意图/空白 JSON 处理 |
| `WebhookSignatureServiceTest` | `inbound/webhook/WebhookSignatureServiceTest.java` | HMAC-SHA256/512 签名、默认算法、不同 secret/算法签名差异 |
| `AccountControllerTest` | `admin/controller/AccountControllerTest.java` | 租户创建接口 |
| `ChannelControllerTest` | `channel/controller/ChannelControllerTest.java` | 渠道主体与渠道账号创建、企微回调 echo 校验 |
| `ChannelAccountHeartbeatTest` | `channel/ChannelAccountHeartbeatTest.java` | 心跳更新账号在线/风险状态、超时离线检测 |
| `WeComSessionArchiveTest` | `channel/controller/WeComSessionArchiveTest.java` | 企微 `msgaudit_notify` 回调、会话存档拉取并保存消息事件/媒体记录 |
| `WeComSyncTest` | `channel/controller/WeComSyncTest.java` | 企微通讯录同步(人员+渠道身份)、群聊同步(会话+参与者+账号绑定) |
| `CrmControllerTest` | `crm/controller/CrmControllerTest.java` | 联系人、标签、会话的创建与列表查询 |
| `IvrFlowControllerTest` | `flow/controller/IvrFlowControllerTest.java` | IVR 流程的增删改查、发布、下线生命周期 |
| `InboundFlowControllerTest` | `inbound/controller/InboundFlowControllerTest.java` | 入站流程创建/发布、接收消息事件 |
| `SendControllerTest` | `send/controller/SendControllerTest.java` | 发送接口:单聊发送、规则目标 `and/or` 逻辑、发送记录查询 |
| `SendPreviewControllerTest` | `send/controller/SendPreviewControllerTest.java` | 发送预览:会话/规则目标、渠道拆分、账号用量、黑名单命中但不做限流计数 |
| `SendMessageConsumerTest` | `send/mq/SendMessageConsumerTest.java` | MQ 异步消费:成功回调、失败重试、超过最大重试后的失败回调 |
| `TenantWebhookControllerTest` | `admin/controller/TenantWebhookControllerTest.java` | 租户 webhook 配置查询/更新、测试推送成功/失败 |
---
## 2. 集成测试(补齐)
### 2.1 现有集成测试缺口
- 现有 controller 级测试大多聚焦单接口或局部链路,缺少**从租户创建到发送记录生成**的完整出站链路。
- 缺少**从消息事件接收到 webhook 推送**的完整入站链路验证。
### 2.2 新增集成测试
#### `SendEndToEndTest.java`
- **路径**`backend/mci-server/src/test/java/com/sino/mci/send/service/SendEndToEndTest.java`
- **技术**`@SpringBootTest` + `@AutoConfigureMockMvc` + `@Transactional`直接复用真实数据库Docker 中 MySQL
- **覆盖步骤**
1. 创建租户并登录,获取 `authToken`
2. 创建渠道主体wecom
3. 创建渠道账号并关联主体;
4. 创建会话并关联主体;
5. 绑定账号为会话发送主号;
6. 调用 `/api/v1/send/test` 模拟发送;
7. 查询 `/api/v1/send/{taskId}/records`,校验记录数、状态、渠道类型、会话/账号 ID、内容。
#### `InboundEndToEndTest.java`
- **路径**`backend/mci-server/src/test/java/com/sino/mci/inbound/engine/InboundEndToEndTest.java`
- **技术**`@SpringBootTest` + `@AutoConfigureMockMvc` + `@Transactional`,通过 `@TestConfiguration``RestTemplate` bean 替换为 mock。
- **覆盖步骤**
1. 创建租户并登录;
2. 配置租户入站 webhook URL 与签名密钥;
3. 创建并发布入站流程:`receive -> intent -> webhook`
4. 调用 `/api/v1/inbound/receive` 发送消息事件;
5. 校验返回事件 `isProcessed=1`
6. 验证 `RestTemplate.exchange` 被调用,且目标 URL 为租户配置的 webhook URL。
---
## 3. API 冒烟测试
> 基础地址:`http://localhost:8080/msg-platform`
> 所有业务接口需携带 `X-Tenant-Code` 与 `Authorization: Bearer <token>`。
### 3.1 健康检查
```bash
curl -fsS http://localhost:8080/msg-platform/api/v1/health
```
### 3.2 创建租户
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/account/tenant \
-H 'Content-Type: application/json' \
-d '{"tenantCode":"rescue","tenantName":"救援业务"}'
```
### 3.3 登录(获取 token
```bash
TOKEN=$(curl -s -X POST http://localhost:8080/msg-platform/api/v1/account/login \
-H 'X-Tenant-Code: rescue' \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin123"}' | jq -r '.data.token')
echo "Bearer $TOKEN"
```
### 3.4 渠道主体 CRUD
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/channel-subject \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"channelType":"wecom","subjectCode":"sino","subjectName":"Sino"}'
curl -s http://localhost:8080/msg-platform/api/v1/channel-subject/list \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN"
```
### 3.5 渠道账号创建
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/channel-account \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"accountType":1,"channelType":"wecom","accountId":"ZhangSan","accountName":"张三"}'
```
### 3.6 会话创建与账号绑定
```bash
CONV_ID=$(curl -s -X POST http://localhost:8080/msg-platform/api/v1/conversation \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"conversationType":2,"channelType":"wecom","channelConversationId":"room-001","conversationName":"测试群"}' | jq -r '.data.id')
curl -X POST "http://localhost:8080/msg-platform/api/v1/conversation/$CONV_ID/bind-account" \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"accountId":1,"bindingType":1,"sortOrder":1}'
```
### 3.7 发送消息(模拟发送)
```bash
TASK_ID=$(curl -s -X POST http://localhost:8080/msg-platform/api/v1/send/test \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d "{\"targetType\":\"conversation\",\"targetValue\":{\"conversation_id\":$CONV_ID},\"contentType\":\"text\",\"content\":{\"text\":\"hello\"},\"channelStrategy\":{\"channel_type\":\"wecom\",\"strategy\":\"primary\"}}" | jq -r '.data.taskId')
curl -s "http://localhost:8080/msg-platform/api/v1/send/$TASK_ID/records" \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN"
```
### 3.8 入站消息接收
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/inbound/receive \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"eventId":"evt-001","channelType":"wecom","eventType":"message","messageSource":"realtime","conversationId":1,"personId":1,"content":{"type":"text","body":"我要救援"}}'
```
### 3.9 入站流程管理
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/inbound/flow \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"flowCode":"inbound_default","flowName":"默认入站流程","flowType":"inbound","definitionJson":{"nodes":[{"id":"n1","type":"receive"},{"id":"n2","type":"intent"},{"id":"n3","type":"webhook"}],"edges":[{"source":"n1","target":"n2"},{"source":"n2","target":"n3"}]}}'
curl -X POST http://localhost:8080/msg-platform/api/v1/inbound/flow/inbound_default/publish \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN"
```
### 3.10 租户 webhook 配置
```bash
curl -X POST http://localhost:8080/msg-platform/api/v1/account/tenant/webhook \
-H 'X-Tenant-Code: rescue' \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"inbound_webhook_url":"https://example.com/webhook","auth_config":{"sign_algorithm":"hmac-sha256","webhook_secret":"secret-123"}}'
```
---
## 4. 前端手工测试
前端地址:`http://localhost`
| 页面 | 路由 | 测试步骤 | 预期结果 |
|------|------|---------|---------|
| 登录 | `/login` | 输入租户编码、用户名 `admin`、密码 `admin123` 登录 | 跳转 `/dashboard`,本地存储 token |
| 渠道主体 | `/channel-subject` | 新增 wecom 主体 → 保存 → 刷新列表 → 编辑主体名称 | 列表展示新增/更新后的主体,无报错 |
| 渠道账号 | `/channel-account` | 选择主体 → 新增企微内部账号 → 保存 → 触发同步通讯录/群聊 | 账号列表更新,同步返回成功提示 |
| 会话/群聊 | `/conversation` | 新增群聊 → 绑定发送/接收账号 → 绑定标签 | 会话详情正确显示绑定关系 |
| 发送策略 | `/send-policy` | 新建策略组 → 添加限流/时间窗/黑名单规则 → 保存 | 策略列表展示,规则 JSON 可预览 |
| 入站 Webhook | `/inbound-webhook` | 配置 URL 与签名密钥 → 点击测试推送 | 页面提示推送结果200/失败原因) |
| 入站流程编排 | `/inbound-flow` | 拖拽 receive / intent / condition / webhook 节点 → 连线 → 保存并发布 | 流程定义持久化,发布状态变为已发布 |
| 发送记录 | `/send-record` | 发送消息后进入页面 → 按任务 ID/时间/状态筛选 | 列表展示发送记录及状态 |
| IVR 流程 | `/ivr-flow` | 新建 IVR 流程 → 配置 start / menu 节点 → 保存/发布/下线 | 生命周期状态正常切换 |
| 消息测试 | `/message-test` | 选择会话 → 填写内容 → 点击发送 | 返回任务 ID发送记录可查询 |
---
## 5. Chrome DevTools 测试场景
### 5.1 通用检查项
1. 打开 `DevTools → Console`,切换页面/提交表单,确认无未处理异常。
2. 打开 `Network` 面板,过滤 `XHR/Fetch`,确认所有 API 请求:
- 携带 `X-Tenant-Code`
- 携带 `Authorization: Bearer <token>`
- 返回 `code: 0`(业务成功)。
3. 检查请求/响应 Payload 与架构文档字段是否一致snake_case / camelCase
4. 模拟弱网:在 `Network` 面板选择 `Slow 3G`,观察加载状态与超时提示。
5. 模拟后端 500`Network` 面板对 `/api/v1/send/test` 请求右键 `Block request URL`,观察前端错误处理。
### 5.2 页面级场景
| 页面 | DevTools 操作 | 验证点 |
|------|---------------|--------|
| `/login` | 登录后查看 `Application → Local Storage` | `auth-store` 中保存 token、tenantCode、userInfo |
| `/channel-subject` | 新增主体后查看 Network | POST `/api/v1/channel-subject` 返回 `code: 0`,列表 GET 包含新主体 |
| `/channel-account` | 点击同步通讯录 | POST `/api/v1/channel-account/{id}/sync-contacts` 返回 `syncedCount` |
| `/conversation` | 绑定账号 | POST `/api/v1/conversation/{id}/bind-account` 请求体字段正确 |
| `/send-policy` | 保存策略 | POST `/api/v1/policy-group` 请求体 `policies` JSON 与表单一致 |
| `/inbound-webhook` | 点击测试推送 | POST `/api/v1/account/tenant/webhook/test` 返回 `success``message` |
| `/inbound-flow` | 保存/发布流程 | POST `/api/v1/inbound/flow``/publish` 顺序正确payload 含 nodes/edges |
| `/message-test` | 发送消息 | POST `/api/v1/send/test` 返回 `taskId`,随后 GET `/api/v1/send/{taskId}/records` 返回状态 2 |
| `/send-record` | 筛选/分页 | GET `/api/v1/send/records` 含 page/size 参数,响应 `records` 非空 |
| `/ivr-flow` | 发布/下线 | POST `/api/v1/flow/{code}/publish``/offline` 后状态码正确 |
---
## 6. Docker 部署测试
### 6.1 启动基础设施
```bash
cd /Users/marsal/Projects/sino-project/sino-mci
cp .env.example .env
# 按需编辑 .env
docker compose --profile infra up -d
```
等待所有 `infra` 服务 healthy
```bash
docker compose --profile infra ps
```
### 6.2 启动应用服务
```bash
docker compose --profile app up -d
```
### 6.3 验证健康检查
```bash
# MySQL
docker exec mci-mysql mysqladmin ping -h localhost -u root -proot
# Redis
docker exec mci-redis redis-cli ping
# RabbitMQ
docker exec mci-rabbitmq rabbitmq-diagnostics ping
# mci-server
curl -fsS http://localhost:8080/msg-platform/api/v1/health
# channel-service
curl -fsS http://localhost:8000/health
# admin-web通过首页判断
curl -fsS -o /dev/null -w '%{http_code}' http://localhost/
```
### 6.4 查看日志
```bash
docker compose --profile app logs -f mci-server
docker compose --profile app logs -f channel-service
```
---
## 7. 标准测试命令与结果
### 7.1 命令
```bash
cd /Users/marsal/Projects/sino-project/sino-mci/backend
docker run --rm --network sino-mci_mci-network --dns 8.8.8.8 \
-v "$HOME/.m2:/root/.m2" -v "$(pwd):/app" -w /app \
-e SPRING_DATASOURCE_URL='jdbc:mysql://mci-mysql:3306/sino_mci?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true' \
-e SPRING_DATASOURCE_USERNAME=root -e SPRING_DATASOURCE_PASSWORD=root \
-e SPRING_DATA_REDIS_HOST=mci-redis -e SPRING_RABBITMQ_HOST=mci-rabbitmq \
maven:3.9-eclipse-temurin-21-alpine \
mvn -B -pl mci-server -am test
```
### 7.2 运行结果
- **总测试数**59
- **失败**0
- **错误**0
- **跳过**0
- **构建状态**`BUILD SUCCESS`
新增测试类 `SendEndToEndTest``InboundEndToEndTest` 均通过。
---
## 8. 后续可补充项
- 引入 Testcontainers 或 H2 以支持无 Docker 环境的本地单元测试运行。
-`channel-service` 增加 Python 侧端到端测试。
- 补充前端自动化测试Playwright / Cypress覆盖第 4、5 节手工场景。
- 增加性能/稳定性测试高并发发送、MQ 堆积恢复、会话存档大批量拉取。