v3.1 + 批次0: 智能回复重构基线 - ApprovalMatcher + 关键词降级 + 文档速修 + v4.0任务书面化
This commit is contained in:
@@ -1,176 +0,0 @@
|
||||
# E2E 验收清单 v0.7.0(扫码登录 + MFA)
|
||||
|
||||
> 部署完 v0.7.0 后,**逐项打勾**。任何一项 ❌ 立即回滚。
|
||||
> 每项给出预期结果 + 验证方法 + 失败处理。
|
||||
|
||||
---
|
||||
|
||||
## 0. 部署完成(用户跑过 DEPLOY-LOGIN-MIGRATION-v0.7.0.md 全部步骤)
|
||||
|
||||
- [ ] 后端 `alembic upgrade head` 跑通(head = `025_messages_id_uuid`)
|
||||
- [ ] 4 端 dist 已上传到宿主机 `/opt/wecom-it-desk/frontend-*/dist/`
|
||||
- [ ] nginx `nginx -t` 通过 + `nginx -s reload` 完成
|
||||
- [ ] `docker restart wecom_it_backend` 成功
|
||||
- [ ] 容器状态 `docker ps` 显示 backend/redis/postgres 全部 Up
|
||||
|
||||
---
|
||||
|
||||
## 1. 扫码登录(Phase 1.1 / 1.2 / 1.3)
|
||||
|
||||
### 1.1 门户页面加载
|
||||
- [ ] 浏览器打开 `https://<生产域名>/itportal/`
|
||||
- [ ] 看到 QrcodeLogin 页面(二维码 + 倒计时)
|
||||
- [ ] 不再显示旧的"账号密码"登录
|
||||
|
||||
### 1.2 二维码生成
|
||||
- [ ] 倒计时从 120 秒开始
|
||||
- [ ] 刷新按钮可用
|
||||
- [ ] DevTools Network: `POST /api/auth_qrcode/create` 返回 200 + ticket
|
||||
|
||||
### 1.3 扫码
|
||||
- [ ] 用企微扫 → 企微 OAuth2 跳回 callback
|
||||
- [ ] 门户页面状态从 `waiting` → `scanned`(显示"已扫码,等待确认")
|
||||
- [ ] DevTools Network: `POST /api/auth_qrcode/scan` 成功
|
||||
|
||||
### 1.4 坐席确认
|
||||
- [ ] 已登录坐席在 `/itagent/` 收到确认弹窗
|
||||
- [ ] 点"确认"→ 门户 `waiting` → `confirmed` → 跳转 `/itagent/`
|
||||
- [ ] localStorage 有 `agent_token` / `portal_token`
|
||||
|
||||
### 1.5 角色分发
|
||||
- [ ] 双角色坐席(admin+agent)→ 跳 `/itportal/select`
|
||||
- [ ] 仅 admin → 跳 `/itadmin/`
|
||||
- [ ] 仅 agent → 跳 `/itagent/`
|
||||
- [ ] 仅 user → 跳 `/itdesk/`
|
||||
|
||||
### 1.6 过期处理
|
||||
- [ ] 120 秒不扫 → 状态变 `expired` + 提示"二维码已过期,请刷新"
|
||||
|
||||
---
|
||||
|
||||
## 2. MFA 绑定(Phase 2.4)
|
||||
|
||||
### 2.1 绑定入口
|
||||
- [ ] 坐席登录后 → 顶栏头像 → "绑定 MFA"
|
||||
- [ ] 进 `/itagent/mfa-bind` 页面
|
||||
|
||||
### 2.2 扫码绑定
|
||||
- [ ] 看到 TOTP 二维码(otpauth://totp/...)
|
||||
- [ ] 用 Google Authenticator / 微软 Authenticator 扫
|
||||
- [ ] 输入 6 位 OTP → 点"验证" → 成功
|
||||
- [ ] 页面显示"已绑定" + 备份信息
|
||||
|
||||
### 2.3 API 验证
|
||||
- [ ] `GET /api/mfa/status` 返回 `bound: true, enabled: true`
|
||||
- [ ] `GET /api/mfa/users` (admin) 看到该坐席 bound=true
|
||||
|
||||
---
|
||||
|
||||
## 3. MFA 验证(高危操作守卫)
|
||||
|
||||
### 3.1 30 分钟有效期
|
||||
- [ ] 坐席 admin 角色登录 → 绑 MFA → 调 `/api/admin/high-risk/demo/role_change`
|
||||
- [ ] **未先调 /api/mfa/verify** → 返回 `2001 需要 OTP`
|
||||
- [ ] 调 `POST /api/mfa/verify {otp_code: "123456"}` → 成功
|
||||
- [ ] **再调** 高危端点 → 200 通过
|
||||
- [ ] 等 31 分钟 → 再次调 → 又返回 2001(TTL 失效)
|
||||
|
||||
### 3.2 5 类高危操作
|
||||
- [ ] `POST /api/admin/high-risk/demo/role_change` → 200
|
||||
- [ ] `POST /api/admin/high-risk/demo/config_change` → 200
|
||||
- [ ] `POST /api/admin/high-risk/demo/data_export` → 200
|
||||
- [ ] `POST /api/admin/high-risk/demo/account_disable` → 200
|
||||
- [ ] `POST /api/admin/high-risk/demo/account_create_reset` → 200
|
||||
|
||||
### 3.3 角色拒绝
|
||||
- [ ] 非 admin 角色调高危端点 → 4003 仅管理员
|
||||
|
||||
### 3.4 白名单查询
|
||||
- [ ] `GET /api/admin/high-risk/whitelist` 返回 5 类元数据
|
||||
|
||||
---
|
||||
|
||||
## 4. P0/P1 合规验证
|
||||
|
||||
### 4.1 WebSocket 连接
|
||||
- [ ] H5 员工端开 DevTools → Network → WS
|
||||
- [ ] WS 连接建立,**没有 1006 / missing argument 错误**
|
||||
- [ ] 坐席发消息 → H5 端 100ms 内收到(无轮询 3-5s 延迟)
|
||||
|
||||
### 4.2 消息 ID 类型
|
||||
- [ ] `psql -d wecom_it -c 'SELECT id FROM messages LIMIT 1;'` 返回 UUID 格式
|
||||
- [ ] 前端消息轮询不再偶发 500
|
||||
- [ ] 跨会话消息不再串号
|
||||
|
||||
### 4.3 nginx access_log
|
||||
- [ ] `docker exec wecom_it_nginx tail /var/log/nginx/access.log | head -3`
|
||||
- [ ] 不包含 `Authorization:` / `Cookie:` 字样
|
||||
- [ ] 只剩 IP / method / path / status
|
||||
|
||||
### 4.4 Gitea token
|
||||
- [ ] `cat .git/config | grep 5ad83d` 返回空(token 已撤销)
|
||||
- [ ] `git push` 试一下:**应该失败**(无 push 权限,符合预期)
|
||||
|
||||
---
|
||||
|
||||
## 5. 端到端业务流(回归)
|
||||
|
||||
### 5.1 H5 → 坐席 完整流程
|
||||
- [ ] H5 员工发起会话 → 排队
|
||||
- [ ] 坐席收到分配 → WS 推送
|
||||
- [ ] 坐席发消息 → 员工 < 100ms 收到
|
||||
- [ ] 转人工、邀请、满意度流程无 regression
|
||||
|
||||
### 5.2 管理员后台
|
||||
- [ ] 仪表盘加载正常
|
||||
- [ ] 坐席管理 CRUD 正常
|
||||
- [ ] 功能开关可切换
|
||||
- [ ] 集成配置 6 个系统显示完整
|
||||
- [ ] MFA 管理页 `/mfa-manage` 表格可搜索/过滤/分页
|
||||
- [ ] 重置 MFA 按钮可弹 ElMessageBox 二次确认
|
||||
|
||||
### 5.3 端点路径(临时 4 xfail)
|
||||
- [ ] `POST /api/conversations/{id}/messages` **仍 404** — pre-existing,不影响生产
|
||||
- [ ] 实际走 H5 的 `/api/h5/conversations/current/messages` 路径
|
||||
|
||||
---
|
||||
|
||||
## 6. 性能与稳定性
|
||||
|
||||
- [ ] 长时间压测(可选): `wrk -t4 -c100 -d60s https://<域>/api/auth_qrcode/create`
|
||||
- [ ] 无 5xx 错误
|
||||
- [ ] Redis 连接稳定(无 timeout)
|
||||
- [ ] PG CPU < 50%
|
||||
|
||||
---
|
||||
|
||||
## 7. 回滚预案
|
||||
|
||||
如果任意 ❌ 项:
|
||||
|
||||
```bash
|
||||
# 1. 停止后端
|
||||
sudo docker stop wecom_it_backend
|
||||
|
||||
# 2. 恢复 4 端 dist
|
||||
sudo cp -r /opt/wecom-it-desk/dist-backup-*/* /opt/wecom-it-desk/frontend-*/dist/
|
||||
|
||||
# 3. 回滚 alembic(只回 025,022/023 保留)
|
||||
sudo docker start wecom_it_backend
|
||||
sudo docker exec wecom_it_backend alembic downgrade 024
|
||||
|
||||
# 4. nginx 回滚
|
||||
sudo docker exec wecom_it_nginx nginx -s reload
|
||||
```
|
||||
|
||||
详见 `DEPLOY-LOGIN-MIGRATION-v0.7.0.md` 末尾"回滚预案"。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 验收人签字
|
||||
|
||||
| 角色 | 姓名 | 日期 | 结果 |
|
||||
|---|---|---|---|
|
||||
| 部署 | | | |
|
||||
| 验收 | | | |
|
||||
| 复核 | | | |
|
||||
@@ -1,316 +0,0 @@
|
||||
# 智能IT支持服务台 — 综合 QA 测试报告
|
||||
|
||||
> 本文档合并历次 QA 测试报告,按时间倒序排列(最新在前)。
|
||||
|
||||
---
|
||||
|
||||
## 报告索引
|
||||
|
||||
| # | 测试日期 | 报告名称 | 测试范围 | 通过率 | 状态 |
|
||||
|---|----------|----------|----------|--------|------|
|
||||
| 1 | 2026-07-11 | 复杂场景重构 P2/P3 单元测试 | TokenCounter/SnapshotService/CorrectionService/ContextCompressor/Models/Schemas/Constants | 48/48 | ✅ 通过 |
|
||||
| 2 | 2026-06-03 | WebSocket 实时推送功能 QA | WS 连接/心跳/重连/广播 | 10/11 (1跳过) | ✅ 通过 |
|
||||
| 3 | 2025-07-04 | 坐席工作台 v5.3 QA | T01-T04 增量代码 | 36/42 (4失败/2警告) | ⚠️ 有条件通过 |
|
||||
|
||||
---
|
||||
|
||||
## 一、复杂场景重构 P2/P3 单元测试报告
|
||||
|
||||
> 测试日期: 2026-07-11 | QA工程师: 严过关(Yan)| 测试范围: P2 上下文压缩 + P3 多轮纠错
|
||||
|
||||
### 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 测试文件 | `backend/tests/test_p2_p3.py` |
|
||||
| 总用例数 | 48 |
|
||||
| 通过 | 48 |
|
||||
| 失败 | 0 |
|
||||
| **IS_PASS** | **YES** |
|
||||
|
||||
**路由判定**: Send To NoOne — 全部通过,无需工程师修复。
|
||||
|
||||
### 测试环境
|
||||
|
||||
- Python 3.12 + pytest + pytest-asyncio
|
||||
- DEV_MODE=true,SQLite 内存数据库
|
||||
- 依赖:tiktoken 0.13.0(已安装),无 LLM 客户端(降级模式测试)
|
||||
|
||||
### 1. TokenCounter 测试(7 项)
|
||||
|
||||
> 模块: `app/utils/token_counter.py` | 测试类: `TestTokenCounter`
|
||||
|
||||
| TC_ID | 测试场景 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|------|
|
||||
| TC-001 | 空文本/None 返回 0 | 调用 `count_tokens("")` 和 `count_tokens(None)` | 均返回 0 | ✅ PASS |
|
||||
| TC-002 | 非空文本返回正数 | 调用 `count_tokens("Hello world")` 和 `count_tokens("你好世界")` | 均返回正整数 | ✅ PASS |
|
||||
| TC-003 | 长文本 token 数大于短文本 | 对比 `"hi"` 与 100 倍长文本的 token 数 | long > short | ✅ PASS |
|
||||
| TC-004 | 消息列表 token 计数含 overhead | 2 条消息调用 `count_messages_tokens()` | 总数 = 文本 token 之和 + 8(每条 4 token overhead) | ✅ PASS |
|
||||
| TC-005 | 空消息列表返回 0 | 调用 `count_messages_tokens([])` | 返回 0 | ✅ PASS |
|
||||
| TC-006 | is_precise 返回布尔值 | 调用 `TokenCounter.is_precise()` | 返回 `True` 或 `False` | ✅ PASS |
|
||||
| TC-007 | 相同输入幂等性 | 同一文本调用两次 `count_tokens()` | 两次结果相等 | ✅ PASS |
|
||||
|
||||
### 2. SnapshotService 测试(7 项)
|
||||
|
||||
> 模块: `app/services/automation/snapshot_service.py` | 测试类: `TestSnapshotService`
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|------|
|
||||
| TC-008 | 创建快照 | mock 1 个信息项 | 调用 `create_snapshot("session-1", "工号", [])` | 返回 snapshot 对象,trigger_item_key="工号",is_undone=False,db.add+flush 各调用 1 次 | ✅ PASS |
|
||||
| TC-009 | 多信息项快照 | mock 2 个信息项(工号+姓名) | 同上 | 快照创建成功,trigger_item_key 正确 | ✅ PASS |
|
||||
| TC-010 | 撤销次数超限 | mock 已撤销次数 = MAX_UNDO_COUNT(5) | 调用 `undo_correction("session-1")` | 抛出 ValueError,包含"超限" | ✅ PASS |
|
||||
| TC-011 | 无可撤销快照 | mock 已撤销=0 + 无未撤销快照 | 调用 `undo_correction("session-1")` | 抛出 ValueError,包含"无可撤销" | ✅ PASS |
|
||||
| TC-012 | 空快照历史 | mock 返回空列表 | 调用 `get_snapshot_history("session-1")` | 返回 `[]` | ✅ PASS |
|
||||
| TC-013 | 版本对比时信息项不存在 | mock 返回 None | 调用 `get_version_diff("session-1", "不存在项", 1, 2)` | 抛出 ValueError,包含"不存在" | ✅ PASS |
|
||||
| TC-014 | 无最新快照 | mock 返回 None | 调用 `get_latest_snapshot("session-1")` | 返回 None | ✅ PASS |
|
||||
|
||||
### 3. CorrectionService 测试(7 项)
|
||||
|
||||
> 模块: `app/services/automation/correction_service.py` | 测试类: `TestCorrectionService`
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|------|
|
||||
| TC-015 | 空更正列表抛异常 | 无 | 调用 `batch_correct("session-1", [])` | 抛出 Exception | ✅ PASS |
|
||||
| TC-016 | 依赖检查——有依赖 | mock 1 个 derived_from=["工号"] 的信息项 | 调用 `check_dependencies("session-1", "工号")` | 返回 1 条 warning,item_key="设备分配人",derived_from="工号" | ✅ PASS |
|
||||
| TC-017 | 依赖检查——无依赖 | mock 1 个 derived_from=None 的信息项 | 同上 | 返回空列表 `[]` | ✅ PASS |
|
||||
| TC-018 | 空版本链(信息项不存在) | mock 返回 None | 调用 `get_version_chain("session-1", "不存在项")` | 返回 `[]` | ✅ PASS |
|
||||
| TC-019 | 带历史记录的版本链 | mock 信息项 version=3 + update_history 2 条 | 调用 `get_version_chain("session-1", "工号")` | 返回 3 条记录(2 历史 + 1 当前),首条 version=1,末条 action="current" | ✅ PASS |
|
||||
| TC-020 | 空更正历史 | mock 返回空列表 | 调用 `get_correction_history("session-1")` | 返回 `[]` | ✅ PASS |
|
||||
| TC-021 | 批量更正(隐含通过 TC-019 覆盖) | TC-019 已验证版本链完整性 | — | — | ✅ PASS |
|
||||
|
||||
### 4. ContextCompressor 测试(14 项)
|
||||
|
||||
> 模块: `app/services/automation/context_compressor.py` | 测试类: `TestContextCompressor`
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|------|
|
||||
| TC-022 | 未超阈值不压缩 | 默认阈值 6000 | 1 条消息调用 `should_compress()` | 返回 False | ✅ PASS |
|
||||
| TC-023 | 超阈值触发压缩 | 阈值设为 10 | 10 倍长文本消息调用 `should_compress()` | 返回 True | ✅ PASS |
|
||||
| TC-024 | 空消息列表不压缩 | 无 | 调用 `should_compress([])` | 返回 False | ✅ PASS |
|
||||
| TC-025 | count_tokens 方法 | 1 条消息 | 调用 `count_tokens(messages)` | 返回正数 | ✅ PASS |
|
||||
| TC-026 | 空消息 token 数为 0 | 无 | 调用 `count_tokens([])` | 返回 0 | ✅ PASS |
|
||||
| TC-027 | 空信息项提取 | 无信息项 + 无动作 | 调用 `_extract_key_info([], [], "")` | info_items 和 actions 均包含"暂无" | ✅ PASS |
|
||||
| TC-028 | 有信息项正确提取 | mock 1 个已填写信息项 + 1 个动作 | 同上 | info_items 包含名称/值/版本号,actions 包含标题和状态图标 | ✅ PASS |
|
||||
| TC-029 | 未填写信息项被过滤 | mock 1 个 is_filled=False 的信息项 | 同上 | 显示"暂无"(未填写项被过滤) | ✅ PASS |
|
||||
| TC-030 | 获取最近 N 轮对话 | 6 条消息(3 轮) | 调用 `_get_recent_messages(messages, 2)` | 返回 4 条消息,末条为最新回复 | ✅ PASS |
|
||||
| TC-031 | 空消息列表获取最近 | 无 | 调用 `_get_recent_messages([], 2)` | 返回 `[]` | ✅ PASS |
|
||||
| TC-032 | 请求轮数超过实际 | 2 条消息(1 轮) | 调用 `_get_recent_messages(messages, 5)` | 返回 2 条(全部可用消息) | ✅ PASS |
|
||||
| TC-033 | 格式化空消息 | 无 | 调用 `_format_recent_messages([])` | 结果包含"无" | ✅ PASS |
|
||||
| TC-034 | 格式化非空消息 | 2 条消息 | 同上 | 结果包含 role 和 content | ✅ PASS |
|
||||
| TC-035 | 降级截断方法 | 4 条消息 | 调用 `_truncate_messages(messages, [], [], "")` | 返回 ≥1 条,首条 role="system",content 包含"降级" | ✅ PASS |
|
||||
|
||||
### 5. ContextCompressor 集成测试(1 项)
|
||||
|
||||
> 异步测试,验证完整压缩流程
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|------|
|
||||
| TC-036 | 无 LLM 客户端完整压缩 | 阈值=5,20 条消息(10 轮) | 调用 `compress("session-1", messages, [], [])` | 返回包含 compressed_messages/tokens_before/tokens_after/compression_ratio/compression_level/summary/duration_ms,压缩后消息数 ≤ 原消息数 | ✅ PASS |
|
||||
|
||||
### 6. 数据模型测试(3 项)
|
||||
|
||||
> 模块: `app/models/automation.py` | 测试类: `TestP2P3Models`
|
||||
|
||||
| TC_ID | 测试场景 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|------|
|
||||
| TC-037 | ContextCompression 模型字段 | 检查 `__tablename__` 和列名集合 | 表名 = "auto_context_compressions",包含 id/session_id/tokens_before/tokens_after/compression_ratio/task_node/duration_ms/compression_level/summary/created_at | ✅ PASS |
|
||||
| TC-038 | InformationSnapshot 模型字段 | 同上 | 表名 = "auto_information_snapshots",包含 id/session_id/trigger_item_key/snapshot_data/correction_ids/is_undone/created_at | ✅ PASS |
|
||||
| TC-039 | InformationItem P2/P3 新增字段 | 检查列名集合 | 包含 `derived_from` 和 `correction_reason` | ✅ PASS |
|
||||
|
||||
### 7. Schema 测试(7 项)
|
||||
|
||||
> 模块: `app/schemas/automation.py` | 测试类: `TestP2P3Schemas`
|
||||
|
||||
| TC_ID | 测试场景 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|------|
|
||||
| TC-040 | BatchCorrectRequest Schema | 构造 1 条更正 + reason | corrections 长度=1,reason 正确 | ✅ PASS |
|
||||
| TC-041 | BatchCorrectResponse Schema | 构造响应含 1 条更正项 + snapshot_id=1 | snapshot_id=1,corrected_items 长度=1 | ✅ PASS |
|
||||
| TC-042 | UndoCorrectionResponse Schema | 构造撤销响应 | remaining_undo_count=4,undone_items 包含"工号" | ✅ PASS |
|
||||
| TC-043 | VersionDiffRequest Schema | 构造 v1=1, v2=2 | v1=1, v2=2 | ✅ PASS |
|
||||
| TC-044 | VersionDiffResponse Schema | 构造对比响应 | changed=True, v1_value="11111" | ✅ PASS |
|
||||
| TC-045 | CompressionLogItem Schema | 构造压缩日志项 | tokens_before=8000, compression_level=2 | ✅ PASS |
|
||||
| TC-046 | CompressionLogListResponse Schema | 构造空列表响应 | total=0, logs=[] | ✅ PASS |
|
||||
|
||||
### 8. 常量测试(3 项)
|
||||
|
||||
> 模块: `app/constants.py` | 测试类: `TestP2P3Constants`
|
||||
|
||||
| TC_ID | 测试场景 | 测试步骤 | 预期结果 | 状态 |
|
||||
|-------|----------|----------|----------|------|
|
||||
| TC-047 | 新增错误码存在 | 检查 AutomationErrorCode 枚举 | COMPRESSION_FAILED=4017, UNDO_LIMIT_EXCEEDED=4018, BATCH_CORRECT_FAILED=4019 | ✅ PASS |
|
||||
| TC-048 | 新增错误消息存在 | 检查 AUTOMATION_ERROR_MESSAGES 字典 | 3 个新错误码均有对应消息 | ✅ PASS |
|
||||
| TC-049 | 错误消息内容正确 | 调用 `automation_error_message(UNDO_LIMIT_EXCEEDED)` | 消息包含"超限"和"撤销" | ✅ PASS |
|
||||
|
||||
### 9. 测试覆盖矩阵
|
||||
|
||||
| 模块 | 文件 | 用例数 | 覆盖率 |
|
||||
|------|------|--------|--------|
|
||||
| TokenCounter | `app/utils/token_counter.py` | 7 | 空/非空/长文本/消息列表/幂等性/精确性 |
|
||||
| SnapshotService | `app/services/automation/snapshot_service.py` | 7 | 创建/多项/撤销超限/无快照/历史/版本对比/最新快照 |
|
||||
| CorrectionService | `app/services/automation/correction_service.py` | 7 | 空更正/依赖检查/无依赖/版本链空/版本链有历史/更正历史 |
|
||||
| ContextCompressor | `app/services/automation/context_compressor.py` | 15 | 阈值判断/空消息/token计数/关键信息提取/最近消息/格式化/截断/完整压缩流程 |
|
||||
| Models | `app/models/automation.py` | 3 | ContextCompression/InformationSnapshot/InformationItem新字段 |
|
||||
| Schemas | `app/schemas/automation.py` | 7 | 请求/响应Schema全覆盖(batch-correct/undo/version-diff/compression-log) |
|
||||
| Constants | `app/constants.py` | 3 | 错误码/错误消息/消息内容 |
|
||||
| **合计** | | **49** | |
|
||||
|
||||
> **注**: 实际 pytest 收集到 48 个测试用例(TC-021 为隐含覆盖,不单独收集)。报告索引中标注 48/48。
|
||||
|
||||
### 10. 生产部署验证
|
||||
|
||||
> 部署日期: 2026-07-11 | 服务器: itsupport.servyou.com.cn
|
||||
|
||||
| 验证项 | 方法 | 结果 |
|
||||
|--------|------|------|
|
||||
| 数据库迁移 049 | PostgreSQL 执行 SQL | ✅ 2 表 + 2 列创建成功 |
|
||||
| tiktoken 安装 | `pip install tiktoken>=0.5.0` | ✅ v0.13.0 |
|
||||
| 后端重启 | `docker compose restart backend` | ✅ healthy |
|
||||
| 6 个新 API 端点 | Docker 网络内 curl | ✅ 全部 200 |
|
||||
| H5 前端页面 | agent-browser 实测 | ✅ 200, 0 JS 错误 |
|
||||
| 坐席端前端页面 | curl 验证 | ✅ 301→200 |
|
||||
|
||||
**6 个新 API 端点**(路由前缀 `/itportal/automation/`):
|
||||
|
||||
| 端点 | 方法 | 路径 | 状态码 |
|
||||
|------|------|------|--------|
|
||||
| 压缩日志查询 | GET | `/itportal/automation/sessions/{id}/compression-logs` | 200 |
|
||||
| 更正历史查询 | GET | `/itportal/automation/sessions/{id}/correction-history` | 200 |
|
||||
| 版本链查询 | GET | `/itportal/automation/sessions/{id}/version-chain/{item}` | 200 |
|
||||
| 批量更正 | POST | `/itportal/automation/sessions/{id}/batch-correct` | 200 |
|
||||
| 撤销更正 | POST | `/itportal/automation/sessions/{id}/undo-correction` | 200 |
|
||||
| 版本对比 | GET | `/itportal/automation/sessions/{id}/corrections/diff` | 200 |
|
||||
|
||||
### 11. 路由决策
|
||||
|
||||
**Send To: NoOne** — 48/48 测试全部通过,6 个生产 API 端点验证通过,前端页面渲染正常。无需工程师修复。
|
||||
|
||||
---
|
||||
|
||||
## 二、WebSocket 实时推送功能 QA 测试报告
|
||||
|
||||
> 测试日期: 2026-06-03 | QA工程师: 严过关(Edward)
|
||||
|
||||
### 总览
|
||||
|
||||
- **测试对象**: WebSocket 实时推送功能(9个文件)
|
||||
- **测试轮次**: 1(第2轮无需执行,所有可测试项均通过)
|
||||
|
||||
### 1. 代码审查结果
|
||||
|
||||
| # | 文件 | 检查结果 | 状态 |
|
||||
|---|------|----------|------|
|
||||
| 1 | `ws_manager.py` — broadcast/send_to_agent 异常处理 | send_to_agent try/except 包裹;broadcast 拷贝 keys 避免遍历异常;connect 旧连接清理 | ✅ PASS |
|
||||
| 2 | `ws.py` — WebSocketDisconnect 处理 | 捕获 WebSocketDisconnect + 通用 Exception,均清理连接 | ✅ PASS |
|
||||
| 3 | `useWebSocket.ts` — 断线重连逻辑 | 指数退避(1s→2s→4s→8s→16s→30s);intentionalDisconnect 标志;心跳30s;WS断连自动降级轮询 | ✅ PASS |
|
||||
| 4 | `message_router.py` / `session_service.py` — WS 广播位置 | 所有广播均在 `db.flush()` 后、`return` 前;try/except 包裹不阻塞主流程 | ✅ PASS |
|
||||
| 5 | `conversation.ts` — handleNewMessage 消息去重 | 通过 `message_id` 去重,避免 WS 推送和轮询重复 | ✅ PASS |
|
||||
| 6 | `vite.config.ts` — WS 代理配置 | `/ws` 代理 `ws: true` 配置正确,与 `/api` 不冲突 | ✅ PASS |
|
||||
| 7 | `Workspace.vue` — connect 和 disconnect 处理 | onMounted 调用 connectWs;onUnmounted 调用 disconnectWs + stopAllPolling;登出时先标记主动断开 | ✅ PASS |
|
||||
|
||||
### 2. 后端启动验证
|
||||
|
||||
| 检查项 | 结果 | 说明 |
|
||||
|--------|------|------|
|
||||
| REST API /health | ✅ PASS | 返回 `{"status":"ok","service":"wecom-it-smart-desk"}` |
|
||||
| REST API /api/conversations | ✅ PASS | 正常返回会话列表 |
|
||||
| WebSocket 端点 /ws/{agent_id} | ✅ PASS | 可建立连接 |
|
||||
|
||||
### 3. WebSocket 功能测试
|
||||
|
||||
| 测试项 | 结果 | 说明 |
|
||||
|--------|------|------|
|
||||
| TEST 1: WebSocket 连接 | ✅ PASS | ws://localhost:8000/ws/qa_test_agent1 连接成功 |
|
||||
| TEST 2: Ping/Pong 心跳 | ✅ PASS | 发送 `{"type":"ping"}` → 收到 `{"type":"pong"}` |
|
||||
| TEST 3: 同一坐席重连替换 | ✅ PASS | 同一 agent_id 第二个连接建立成功 |
|
||||
| TEST 4: 不同坐席多连接 | ✅ PASS | 不同 agent_id 可同时连接 |
|
||||
| TEST 5: WS 广播 - 接单事件 | ⏭️ SKIP | SQLite 锁定导致 API 调用失败,代码审查确认逻辑正确 |
|
||||
| TEST 6: 断开连接后清理 | ✅ PASS | ws3 主动断开后,ws1 仍正常工作 |
|
||||
|
||||
> **TEST 5 跳过说明**: SQLite 数据库在 uvicorn 进程中被锁定,导致写操作失败。这是 SQLite 高并发已知限制,与 WebSocket 代码无关。代码审查已确认广播位置正确。
|
||||
|
||||
### 4. 前端集成验证
|
||||
|
||||
| 检查项 | 结果 | 说明 |
|
||||
|--------|------|------|
|
||||
| 前端编译 (vite build) | ✅ PASS | RC=0,3.77s 构建完成,无 TypeScript 错误 |
|
||||
| Workspace.vue 包含 WS 集成 | ✅ PASS | 导入 useWebSocket,生命周期完整 |
|
||||
|
||||
### 5. 综合评估
|
||||
|
||||
- **通过项 (10/11)**: 所有代码审查项 + WS 连接/心跳/重连/多连接/清理 + 前端编译
|
||||
- **跳过项 (1/11)**: WS 广播端到端验证(代码审查确认逻辑正确)
|
||||
- **未发现源码 Bug**
|
||||
|
||||
### 6. 路由决策
|
||||
|
||||
**Send To: NoOne** — 所有可测试项均通过,代码审查未发现 Bug,无需发送给工程师修复。
|
||||
|
||||
---
|
||||
|
||||
## 三、坐席工作台 v5.3 QA 测试报告(历史归档)
|
||||
|
||||
> 测试日期: 2025-07-04 | QA工程师: 严过关(Yan)| 测试范围: T01-T04 全部增量代码
|
||||
|
||||
### 总览
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| 总检查项 | 42 |
|
||||
| 通过 | 36 |
|
||||
| 失败 | 4 |
|
||||
| 警告 | 2 |
|
||||
| **IS_PASS** | **YES(有条件)** |
|
||||
|
||||
**路由判定**: 源码有 4 处 Bug → **发送给工程师修复**
|
||||
|
||||
### 1. TypeScript 编译检查(7个错误)
|
||||
|
||||
| # | 严重度 | 文件 | 问题 | 修复方案 |
|
||||
|---|--------|------|------|----------|
|
||||
| BUG-1 | 🔴 严重 | `stores/quickReply.ts:153` | `replaceAll` 需要 ES2021+ | 改 `tsconfig.json` lib 为 `ES2021`,或用 `split().join()` 替代 |
|
||||
| BUG-2 | 🟡 低 | `components/chat/UserInfoBar.vue:316` | `emit` 声明未使用 | 用 `emit()` 替代模板 `$emit`,或删除变量声明 |
|
||||
| BUG-3 | 🟡 低 | `stores/conversation.ts:33` | `TagsResult` 导入未使用 | 从 import 移除 |
|
||||
| BUG-4 | 🟡 低 | `stores/conversation.ts:770,792` | `data` 参数未使用 | 改为 `_data` |
|
||||
| BUG-5 | 🟢 信息 | `main.ts:23` | element-plus locale 缺类型声明 | `env.d.ts` 添加 `declare module` |
|
||||
|
||||
### 2. 逻辑 Bug 检查
|
||||
|
||||
| # | 严重度 | 文件 | 问题 | 修复方案 |
|
||||
|---|--------|------|------|----------|
|
||||
| BUG-6 | 🔴 中等 | `UserInfoBar.vue:418` | `turnCount` 运算符优先级错误:`tags?.repeat_count \|\| 0 + 1` 应先计算 `0+1` | 改为 `(tags?.repeat_count \|\| 0) + 1` |
|
||||
| BUG-7 | 🟡 中等 | `UserInfoBar.vue:464` | `Math.random()` 导致 UI 闪烁 | 改为基于 `conversation.id` 的确定性 Mock |
|
||||
|
||||
### 3. CSS 变量一致性(警告级)
|
||||
|
||||
多处硬编码色值未使用 CSS 变量,深色主题下可能显示异常(不影响功能,建议后续迭代统一):
|
||||
|
||||
| 文件 | 硬编码值 | 建议使用 CSS 变量 |
|
||||
|------|----------|-----------------|
|
||||
| `UserInfoBar.vue` | `#FDF6EC` / `#E6A23C` / `#FAECD8` | 黄色 chip — `--color-warning-soft` / `--color-warning` |
|
||||
| `UserInfoBar.vue` | `#FEF0F0` / `#F56C6C` / `#FDE2E2` | 红色 chip — `--color-danger-soft` / `--color-danger` |
|
||||
| `UserInfoBar.vue` | `#F4ECFF` / `#9B59B6` / `#E8D5F5` | 紫色 chip — 新增 `--color-purple-soft` / `--color-purple` |
|
||||
| `FlowchartNode.vue` | `#FDF6EC` / `#E6A23C` / `#FAECD8` | 判断节点 — 同上 |
|
||||
| `TopBar.vue` | `#2b6cb0` | 渐变深色 — `--accent-dark` |
|
||||
| `TopBar.vue` | `#fef0f0` / `#c0392b` / `#e74c3c` | 应急横幅 — 未适配深色模式 |
|
||||
|
||||
### 4. 功能完整性检查
|
||||
|
||||
| 模块 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| T01 主题系统 | ✅ 完成 | CSS变量 + useTheme + Pinia store + TopBar切换按钮 |
|
||||
| T02 左栏改造 | ✅ 完成 | 三段折叠 + 优先级图标 + 待办面板 + TodoStore |
|
||||
| T03 中栏改造 | ✅ 完成 | UserInfoBar + ItLevelBadge + AiRecommendInline + TroubleshootBar + 快捷键 |
|
||||
| T04 右栏改造 | ✅ 完成 | AiAssistantPanel 重写 + QuickReplyPanel 重写 |
|
||||
| 任务详情视图 | ✅ 完成 | TaskDetailView + 三种子视图 |
|
||||
| 后端扩展 | ✅ 完成 | todo_items + troubleshooting_templates + employees API |
|
||||
|
||||
### 5. 最终判定
|
||||
|
||||
**IS_PASS: YES(有条件)**
|
||||
|
||||
**条件**: 工程师需修复 BUG-1(ES2021 target)和 BUG-6(turnCount 优先级),其余为低优先级警告,可在后续迭代修复。
|
||||
|
||||
---
|
||||
|
||||
*合并生成时间: 2026-06-07 | 合并人: 小米*
|
||||
*P2/P3 测试用例补充: 2026-07-11 | 严过关*
|
||||
@@ -1,89 +0,0 @@
|
||||
# 呼叫坐席功能验证指南
|
||||
|
||||
> 后端 `http://localhost:8000` | 前端 `http://localhost:5173`
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
1. 后端 8000 端口已启动 ✅
|
||||
2. 前端 H5 5173 端口已启动 ✅
|
||||
3. 数据库已包含 `ai_substantive_reply_count` 列(项目用 `create_all(checkfirst=True)`,重启后端即自动添加)
|
||||
|
||||
## 测试流程(按顺序验证)
|
||||
|
||||
### 测试1:打招呼被拦截,按钮不出现
|
||||
|
||||
| 步骤 | 操作 | 预期结果 |
|
||||
|------|------|---------|
|
||||
| 1 | 浏览器打开 `http://localhost:5173` | 进入会话窗口 |
|
||||
| 2 | 输入 "你好" 发送 | AI回复引导话术(如"你好!请描述你遇到的IT问题..."),**按钮不出现** |
|
||||
| 3 | 输入 "hi" 发送 | 同上,引导话术,按钮不出现 |
|
||||
|
||||
### 测试2:直接呼叫人工被拦截
|
||||
|
||||
| 步骤 | 操作 | 预期结果 |
|
||||
|------|------|---------|
|
||||
| 4 | 输入 "人工坐席" 发送 | AI回复引导话术(如"请先描述你的问题,AI会先帮你分析..."),**按钮不出现** |
|
||||
| 5 | 输入 "转人工" 发送 | 同上 |
|
||||
|
||||
### 测试3:正常问题 → AI回复1~2次,按钮不出现
|
||||
|
||||
| 步骤 | 操作 | 预期结果 |
|
||||
|------|------|---------|
|
||||
| 6 | 输入 "我的打印机连不上了" | AI给出第1次实质性回复,按钮仍不出现 |
|
||||
| 7 | 输入 "我试了重启还是不行" | AI给出第2次实质性回复,按钮仍不出现 |
|
||||
|
||||
### 测试4:AI回复满3次,按钮出现
|
||||
|
||||
| 步骤 | 操作 | 预期结果 |
|
||||
|------|------|---------|
|
||||
| 8 | 输入 "驱动也重装了还是不行" | AI给出第3次实质性回复,**「👊 呼叫坐席」按钮出现** |
|
||||
| 9 | 检查底部引导文案 | 变为 "👊👊 呼叫坐席通道已开启..." 橙色闪烁 |
|
||||
|
||||
### 测试5:点击按钮 → 弹窗动画
|
||||
|
||||
| 步骤 | 操作 | 预期结果 |
|
||||
|------|------|---------|
|
||||
| 10 | 点击「👊 呼叫坐席」按钮 | 全屏弹窗,直接进入摇人动画(7个场景SVG依次切换) |
|
||||
| 11 | 等待动画播放 | 自动发送 shake 请求,成功后有"已通知坐席"提示 |
|
||||
| 12 | 弹窗自动关闭 | 约4秒后自动关闭,会话进入排队状态 |
|
||||
|
||||
### 测试6:API 直接验证(可选)
|
||||
|
||||
用 curl 验证后端逻辑:
|
||||
|
||||
```bash
|
||||
# 1. 获取/创建当前会话
|
||||
curl -s http://localhost:8000/api/h5/conversations/current -H "X-Employee-Id: test001" | python -m json.tool
|
||||
|
||||
# 检查返回的 can_call_agent 应为 false,ai_substantive_reply_count 应为 0
|
||||
|
||||
# 2. 发送问候语 → 应该收到引导回复
|
||||
curl -s -X POST http://localhost:8000/api/h5/conversations/current/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"employee_id":"test001","content":"你好"}' | python -m json.tool
|
||||
|
||||
# 检查 is_guidance 应为 true,can_call_agent 应为 false
|
||||
|
||||
# 3. 发送实际问题 x3
|
||||
curl -s -X POST http://localhost:8000/api/h5/conversations/current/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"employee_id":"test001","content":"打印机连不上"}' | python -m json.tool
|
||||
|
||||
# 重复3次,第3次后 can_call_agent 应为 true
|
||||
|
||||
# 4. 在未满3次时尝试 shake → 应返回 1003 错误
|
||||
curl -s -X POST http://localhost:8000/api/h5/conversations/current/shake \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Employee-Id: test002" \
|
||||
-d '{}' | python -m json.tool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **每个会话独立计数**:`ai_substantive_reply_count` 是 Conversation 级别的字段,不同用户/会话不共享
|
||||
2. **切换会话会重置**:新会话从 0 开始
|
||||
3. **后续可扩展**:如果用户说"谢谢"等结束语,可以重置计数;当前版本未实现此逻辑
|
||||
@@ -0,0 +1,219 @@
|
||||
# Token多IP异常检测 - 测试用例
|
||||
|
||||
> **任务ID**: 待分配
|
||||
> **关联文档**: 技术设计-Token多IP异常检测.md
|
||||
> **测试环境**: 开发环境 / 测试环境
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试概述
|
||||
|
||||
### 1.1 测试目标
|
||||
|
||||
验证Token多IP异常检测功能的正确性,确保:
|
||||
- 正常多设备使用不触发误报
|
||||
- 异常多IP使用能正确触发告警
|
||||
- 告警内容准确
|
||||
|
||||
### 1.2 测试范围
|
||||
|
||||
| 模块 | 测试内容 |
|
||||
|------|----------|
|
||||
| record_token_ip | IP记录逻辑 |
|
||||
| detect_anomaly | 异常检测逻辑 |
|
||||
| 告警发送 | 企微消息格式 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 测试用例
|
||||
|
||||
### 2.1 单元测试
|
||||
|
||||
#### T001: 单IP使用Token
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T001 |
|
||||
| 场景 | 同一IP多次使用Token |
|
||||
| 预置条件 | Redis中无数据 |
|
||||
| 测试步骤 | 1. 调用 record_token_ip(token="test", ip="10.0.0.1") <br> 2. 获取 token_ips:* key的SCARD值 |
|
||||
| 预期结果 | IP数量=1,不触发告警 |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### T002: 3个IP使用Token (阈值)
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T002 |
|
||||
| 场景 | 3个不同IP使用同一Token |
|
||||
| 预置条件 | Redis中已有token_ips:test包含2个IP |
|
||||
| 测试步骤 | 1. 调用 record_token_ip(token="test", ip="10.0.0.3") <br> 2. 执行 detect_token_anomaly() <br> 3. 检查告警是否发送 |
|
||||
| 预期结果 | IP数量=3,触发告警 |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### T003: 5个IP使用Token (严重)
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T003 |
|
||||
| 场景 | 5个不同IP使用同一Token |
|
||||
| 预置条件 | Redis中已有token_ips:test包含4个IP |
|
||||
| 测试步骤 | 1. 调用 record_token_ip(token="test", ip="10.0.0.5") <br> 2. 执行 detect_token_anomaly() |
|
||||
| 预期结果 | IP数量=5,触发告警(严重) |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### T004: 同一IP多次使用
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T004 |
|
||||
| 场景 | 同一IP多次调用record_token_ip |
|
||||
| 预置条件 | Redis中已有token_ips:test包含1个IP |
|
||||
| 测试步骤 | 1. 调用 record_token_ip(token="test", ip="10.0.0.1") 3次 <br> 2. 获取 token_ips:test 的SCARD值 |
|
||||
| 预期结果 | IP数量仍为1(Set自动去重) |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### T005: Token过期后IP清空
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T005 |
|
||||
| 场景 | Token过期后IP记录清空 |
|
||||
| 预置条件 | Redis中token_ips:test存在,TTL=60秒 |
|
||||
| 测试步骤 | 1. 等待TTL过期 <br> 2. 检查key是否存在 |
|
||||
| 预期结果 | key不存在(自动过期) |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### T006: 重复告警抑制
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | T006 |
|
||||
| 场景 | 同一异常Token在告警周期内再次触发 |
|
||||
| 预置条件 | 已触发过告警,alerted_key存在 |
|
||||
| 测试步骤 | 1. 执行 detect_token_anomaly() <br> 2. 检查告警发送次数 |
|
||||
| 预期结果 | 不重复发送告警 |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
---
|
||||
|
||||
### 2.2 集成测试
|
||||
|
||||
#### I001: 真实API请求IP记录
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | I001 |
|
||||
| 场景 | 通过API请求触发IP记录 |
|
||||
| 预置条件 | 后端服务运行中 |
|
||||
| 测试步骤 | 1. 发送HTTP请求(带X-Forwarded-For) <br> 2. 检查Redis中IP是否记录 |
|
||||
| 预期结果 | X-Forwarded-For的IP被记录 |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### I002: 定时任务完整流程
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | I002 |
|
||||
| 场景 | 定时任务触发完整检测流程 |
|
||||
| 预置条件 | 3个异常Token存在 |
|
||||
| 测试步骤 | 1. 等待定时任务执行(60秒) <br> 2. 检查告警记录 |
|
||||
| 预期结果 | 3条告警发送 |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
#### I003: 企微消息格式
|
||||
|
||||
| 项目 | 内容 |
|
||||
|------|------|
|
||||
| 用例ID | I003 |
|
||||
| 场景 | 告警消息格式正确 |
|
||||
| 预置条件 | webhook配置正确 |
|
||||
| 测试步骤 | 1. 触发告警 <br> 2. 检查企微收到的消息 |
|
||||
| 预期结果 | markdown格式,包含employee_id、ip_count、token_hash |
|
||||
| 实际结果 | |
|
||||
| 状态 | ☐ |
|
||||
|
||||
---
|
||||
|
||||
## 3. 测试数据
|
||||
|
||||
### 3.1 测试Token
|
||||
|
||||
| Token | employee_id | 说明 |
|
||||
|-------|-------------|------|
|
||||
| test-token-001 | user001 | 单IP测试 |
|
||||
| test-token-002 | user002 | 3IP阈值测试 |
|
||||
| test-token-003 | user003 | 5IP严重测试 |
|
||||
| test-token-004 | user004 | 重复告警测试 |
|
||||
|
||||
### 3.2 测试IP
|
||||
|
||||
| IP | 类型 | 说明 |
|
||||
|----|------|------|
|
||||
| 10.0.0.1 | 内网 | 正常IP |
|
||||
| 10.0.0.2 | 内网 | 正常IP |
|
||||
| 10.0.0.3 | 内网 | 异常IP |
|
||||
| 218.75.34.87 | 公网 | 外部IP |
|
||||
| 117.147.35.138 | 公网 | 外部IP |
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试环境配置
|
||||
|
||||
### 4.1 环境变量
|
||||
|
||||
```bash
|
||||
# 测试环境配置
|
||||
TOKEN_ANOMALY_THRESHOLD=3
|
||||
TOKEN_ANOMALY_WINDOW=3600
|
||||
TOKEN_ANOMALY_AUTO_DISABLE=false
|
||||
CONTENT_AUDIT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
|
||||
```
|
||||
|
||||
### 4.2 Redis预置
|
||||
|
||||
```bash
|
||||
# 预置测试数据
|
||||
redis-cli SET "user:token:test-token-001" '{"employee_id":"user001",...}'
|
||||
redis-cli SADD "token_ips:hash001" "10.0.0.1"
|
||||
redis-cli EXPIRE "token_ips:hash001" 3600
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试结果
|
||||
|
||||
| 用例ID | 预期 | 实际 | 状态 | 备注 |
|
||||
|--------|------|------|------|------|
|
||||
| T001 | 通过 | | ☐ | |
|
||||
| T002 | 通过 | | ☐ | |
|
||||
| T003 | 通过 | | ☐ | |
|
||||
| T004 | 通过 | | ☐ | |
|
||||
| T005 | 通过 | | ☐ | |
|
||||
| T006 | 通过 | | ☐ | |
|
||||
| I001 | 通过 | | ☐ | |
|
||||
| I002 | 通过 | | ☐ | |
|
||||
| I003 | 通过 | | ☐ | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 测试签收
|
||||
|
||||
| 角色 | 姓名 | 日期 | 签名 |
|
||||
|------|------|------|------|
|
||||
| 开发 | | | |
|
||||
| 测试 | | | |
|
||||
| 审核 | | | |
|
||||
|
||||
---
|
||||
|
||||
> **编制人**: 威胁检测工程师
|
||||
> **日期**: 2026-07-14
|
||||
@@ -1,91 +0,0 @@
|
||||
# 登录功能测试用例
|
||||
|
||||
> **版本**: v1.0 | **日期**: 2026-07-06 | **状态**: 待执行
|
||||
> **依据文档**: PRD v1.2 §4.5 身份认证与统一入口
|
||||
> **测试环境**: 本地开发环境
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试范围
|
||||
|
||||
| 模块 | 接口 | 说明 |
|
||||
|------|------|------|
|
||||
| 企微免密登录 | `/api/auth_wecom/jsdk-login` | 企微JS-SDK免认证登录 |
|
||||
| 账号密码登录 | `/api/agents/login` | 坐席/管理员账号密码+OTP登录 |
|
||||
| MFA验证 | `/api/mfa/verify` | OTP验证码验证 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 前置条件
|
||||
|
||||
### 2.1 测试账号
|
||||
|
||||
| 角色 | user_id | 密码 | MFA状态 | 说明 |
|
||||
|------|---------|------|---------|------|
|
||||
| 坐席 | `sxn` | `admin123` | 已绑定 | IT支持组组长 |
|
||||
| 管理员 | `sxn` | `admin123` | 已绑定 | 同上,具有admin权限 |
|
||||
| 普通员工 | `test_user` | - | 未绑定 | 仅user角色 |
|
||||
|
||||
### 2.2 环境要求
|
||||
|
||||
- 后端服务运行在 `http://127.0.0.1:8000`
|
||||
- 前端服务:坐席端 `http://127.0.0.1:5177`,管理后台 `http://127.0.0.1:5178`
|
||||
- Redis 服务正常运行
|
||||
- PostgreSQL/SQLite 数据库正常运行
|
||||
|
||||
---
|
||||
|
||||
## 3. 测试用例
|
||||
|
||||
### 3.1 企微免密登录 (/jsdk-login)
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 实际结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|----------|------|
|
||||
| JSDK-01 | 企微用户具有坐席角色,免密登录 | user_id 具有 agent 角色 | 1. 前端调用 jsdk-login 传入 userid<br>2. 后端查询角色列表 | 返回 token 和 roles=["agent"] | | 待测试 |
|
||||
| JSDK-02 | 企微用户具有管理员角色,免密登录 | user_id 具有 admin 角色 | 同上 | 返回 token 和 roles=["admin"] | | 待测试 |
|
||||
| JSDK-03 | 企微用户具有坐席+管理员角色 | user_id 同时具有 agent 和 admin | 同上 | 返回 token 和 roles=["admin","agent"] | | 待测试 |
|
||||
| JSDK-04 | 企微用户仅具有user角色 | user_id 只有 user 角色 | 同上 | 返回 403 错误:"您没有坐席或管理员权限" | | 待测试 |
|
||||
| JSDK-05 | 企微用户无任何角色 | user_id 不在 user_roles 表 | 同上 | 返回 403 错误 | | 待测试 |
|
||||
|
||||
### 3.2 账号密码登录 (/agents/login)
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 实际结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|----------|------|
|
||||
| PWD-01 | 正确账号密码+OTP登录 | 坐席账号、已绑定MFA | 1. 输入正确账号密码<br>2. 点击登录<br>3. 输入正确OTP | 返回 token,进入工作台 | | 待测试 |
|
||||
| PWD-02 | 正确账号密码+错误OTP | 坐席账号、已绑定MFA | 1. 输入正确账号密码<br>2. 点击登录<br>3. 输入错误OTP | 返回错误:"OTP验证码错误" | | 待测试 |
|
||||
| PWD-03 | 正确账号密码+无OTP | 坐席账号、已绑定MFA | 1. 输入正确账号密码<br>2. 点击登录(不输入OTP) | 返回 require_otp: true,提示输入OTP | | 待测试 |
|
||||
| PWD-04 | 错误账号 | 不存在的账号 | 输入错误的user_id | 返回错误:"用户不存在" | | 待测试 |
|
||||
| PWD-05 | 错误密码 | 正确的user_id,错误密码 | 输入错误的password | 返回错误:"本地密码错误" | | 待测试 |
|
||||
| PWD-06 | 账号密码登录(未绑定MFA) | 坐席账号、未绑定MFA | 输入正确的账号密码 | 直接返回 token,无需OTP | | 待测试 |
|
||||
|
||||
### 3.3 MFA 验证
|
||||
|
||||
| TC_ID | 测试场景 | 前置条件 | 测试步骤 | 预期结果 | 实际结果 | 状态 |
|
||||
|-------|----------|----------|----------|----------|----------|------|
|
||||
| MFA-01 | 正确OTP验证码 | 已绑定MFA的坐席 | 调用 /api/mfa/verify | 返回验证成功 | | 待测试 |
|
||||
| MFA-02 | 错误OTP验证码 | 已绑定MFA的坐席 | 输入错误的OTP | 返回验证失败 | | 待测试 |
|
||||
| MFA-03 | 已验证状态(30分钟内) | 之前已通过OTP验证 | 再次调用需要MFA的接口 | 无需再次OTP | | 待测试 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 执行记录
|
||||
|
||||
| 执行日期 | 测试人员 | 环境 | 备注 |
|
||||
|----------|----------|------|------|
|
||||
| 2026-07-06 | | 本地开发环境 | 首轮测试 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 缺陷记录
|
||||
|
||||
| 缺陷ID | 对应TC | 描述 | 严重程度 | 状态 |
|
||||
|--------|--------|------|----------|------|
|
||||
| | | | | |
|
||||
|
||||
---
|
||||
|
||||
## 6. 修订历史
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 修改人 |
|
||||
|------|------|----------|--------|
|
||||
| v1.0 | 2026-07-06 | 初始版本 | Claude |
|
||||
Reference in New Issue
Block a user