Files
wecom_it_smart_desk/docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md
T
Simon 44e77dcb0e chore(docs): docs/ 目录全面重新编号 + 重组
**重构前**(旧编号 02-11):
- docs/02-产品需求/      → 00 产品规划/PRD
- docs/03-技术架构/      → 01-05 子目录散落
- docs/04-原型设计/      → 01-02 产品设计(HTML 原型)
- docs/05-原型设计/      → screens/
- docs/06-测试素材/      → 02-E2E / 03-功能 / 04-版本测试
- docs/07-项目管理/      → 任务说明书/日报/计划
- docs/08-安全审计/      → 审计报告
- docs/09-堡垒运维/      → toolbox / deploy
- docs/10-项目管理/      → 任务说明书(重复)
- docs/11-历史归档/      → deploy-nas-archived

**重构后**(新编号 00-07,语义化):
- docs/00-产品开发流程与文档管理规范.md
- docs/00-版本迭代总览.md
- docs/01-产品文档/      (PRD/原型/认证/会话/AI 服务/坐席/集成)
- docs/02-技术文档/      (技术方案/架构图/重构记录/前端改造/实现配置)
- docs/03-测试文档/      (E2E/功能用例/版本报告/缺陷单)
- docs/04-运维文档/      (部署运维/运维指南)
- docs/05-运营文档/      (品牌推广/用户手册)
- docs/06-安全审计/      (审计报告)
- docs/07-项目管理/      (任务说明书/日报/计划/看板)

**净收益**:
- 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号)
- 消除 02-产品需求 与 10-项目管理 的编号重叠
- 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录)
- 把运维/安全/项目管理从 0X 散落改为 04/06/07

合计 494 文件 + 78495 行 / - 14076 行
2026-08-03 18:46:55 +08:00

468 lines
16 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.
# 测试用例 - 敏感词检测
> **REQ编号**: REQ-通用-004
> **版本**: v1.0
> **日期**: 2026-07-28
> **作者**: 宋献
> **状态**: v0.7.1 已上线(11/13 通过,2 用例待优化)
> **关联文档**:
> - PRD`01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md`
> - 技术方案:`02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md`
> - 看板验真测试报告(历史基线):`03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md`
> - 自动化测试源码:`src/backend/tests/test_content_moderation.py`
---
## 一、用例汇总
| 类别 | 用例数 | 通过 | 失败 | 阻塞 | 备注 |
|------|--------|------|------|------|------|
| 敏感词命中 WARNA | 5 | 4 | 1 | 0 | 含产品决策保护用例 |
| 正常文本 PASSB | 3 | 3 | 0 | 0 | 含空/纯空格/正常 |
| 隐私字段检测(C | 8 | 7 | 1 | 0 | 含中文边界场景 |
| 分类与建议(D | 4 | 4 | 0 | 0 | 含 fixed 建议文案 |
| 边界与异常(E | 4 | 3 | 1 | 0 | 含超长/Unicode |
| 词库动态管理(F | 2 | 1 | 1 | 0 | add/remove 方法未挂路由 |
| E2E 坐席端(G | 2 | 0 | 0 | 2 | 待补 |
| 性能(H | 2 | 0 | 0 | 2 | v1.1 补 |
| 回归(I | 1 | 1 | 0 | 0 | 完整 13 用例重跑 |
| **总计** | **31** | **23** | **4** | **4** | - |
> **历史基线**`test_content_moderation.py` 13 用例,11 通过 2 失败(已部署修复 + 决策保留)。本测试文档在此基础上扩展至 31 用例。
---
## 二、功能测试用例
### 2.A 敏感词命中 WARN
#### TC-001
| TC-ID | TC-001 |
|-------|--------|
| 用例名称 | 命中"投诉我" → WARN 且 matched 含该词 |
| 前置条件 | 1) ContentModerationService 初始化完成<br>2) 自定义词库含"投诉我" |
| 测试步骤 | 1. 调用 `moderate("投诉我")`<br>2. 检查 action / category / matched_words |
| 预期结果 | 1. action == `ModerationAction.WARN`<br>2. category == `ModerationCategory.PROFANITY`<br>3. matched_words == `["投诉我"]`<br>4. suggestion 非空 |
| 自动化 | ✅ `test_moderate_all_known_custom_words_warn`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-002
| TC-ID | TC-002 |
|-------|--------|
| 用例名称 | 命中"你爱找谁找谁" → WARN |
| 前置条件 | 同 TC-001 |
| 测试步骤 | 1. `moderate("你爱找谁找谁")` |
| 预期结果 | 1. action == WARN<br>2. matched_words 含"你爱找谁找谁" |
| 自动化 | ✅ `test_moderate_returns_warn_with_matched_word`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-003
| TC-ID | TC-003 |
|-------|--------|
| 用例名称 | 命中"自己不会百度吗" → WARN |
| 前置条件 | 同 TC-001 |
| 测试步骤 | 1. `moderate("自己不会百度吗")` |
| 预期结果 | 1. action == WARN<br>2. matched_words 含"自己不会百度吗" |
| 自动化 | ✅ 隐含在 `test_moderate_all_known_custom_words_warn` |
| 测试结果 | ✅ 通过 |
#### TC-004
| TC-ID | TC-004 |
|-------|--------|
| 用例名称 | 命中"这点小事" → WARN |
| 前置条件 | 同 TC-001 |
| 测试步骤 | 1. `moderate("这点小事")` |
| 预期结果 | 1. action == WARN<br>2. matched_words 含"这点小事" |
| 自动化 | ✅ 隐含在 `test_moderate_all_known_custom_words_warn` |
| 测试结果 | ✅ 通过 |
#### TC-005
| TC-ID | TC-005 |
|-------|--------|
| 用例名称 | **WARN 不阻断(产品决策保护)** |
| 前置条件 | 1) 命中敏感词 |
| 测试步骤 | 1. `moderate("自己不会百度吗")`<br>2. 检查 action != BLOCK |
| 预期结果 | 1. action == WARN<br>2. action != BLOCK<br>3. is_blocked == False<br>4. is_warned == True |
| 业务说明 | 2026-07-08 决策:v1.0 维持 WARN,不升级 BLOCK |
| 自动化 | ✅ `test_default_action_is_warn_not_block`(已存在) |
| 测试结果 | ✅ 通过(作为"决策保护"测试保留) |
---
### 2.B 正常文本 PASS
#### TC-101
| TC-ID | TC-101 |
|-------|--------|
| 用例名称 | 正常沟通内容 → PASS |
| 前置条件 | 无 |
| 测试步骤 | 1. `moderate("您好,我的电脑无法开机了")` |
| 预期结果 | 1. action == PASS<br>2. category == None<br>3. matched_words == [] |
| 自动化 | ✅ `test_moderate_clean_text_passes`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-102
| TC-ID | TC-102 |
|-------|--------|
| 用例名称 | 空字符串 → PASS |
| 前置条件 | 无 |
| 测试步骤 | 1. `moderate("")` |
| 预期结果 | 1. action == PASS<br>2. matched_words == [] |
| 自动化 | ✅ `test_moderate_empty_string_passes`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-103
| TC-ID | TC-103 |
|-------|--------|
| 用例名称 | 纯空格字符串 → PASS |
| 前置条件 | 无 |
| 测试步骤 | 1. `moderate(" ")` |
| 预期结果 | 1. action == PASS<br>2. matched_words == [] |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
---
### 2.C 隐私字段检测
#### TC-201
| TC-ID | TC-201 |
|-------|--------|
| 用例名称 | 11 位手机号(标准场景)→ phone |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("我的电话13800138000")` |
| 预期结果 | 1. leaked == `["phone"]` |
| 自动化 | ✅ `test_check_privacy_leak_phone`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-202
| TC-ID | TC-202 |
|-------|--------|
| 用例名称 | **中文+手机号(边界修复验证)** |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("电话:13800138000,请回电")` |
| 预期结果 | 1. leaked == `["phone"]`<br>2. **关键验证**`\b` 失效场景下数字边界修复有效 |
| 自动化 | ❌ 看板验真报告指出此类场景失效,需补 |
| 测试结果 | ⚠️ 待补 |
| 优先级 | 🔴 P0(已上线服务,需尽快补齐) |
#### TC-203
| TC-ID | TC-203 |
|-------|--------|
| 用例名称 | 18 位身份证号 → id_card |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("身份证11010119900307123X")` |
| 预期结果 | 1. leaked == `["id_card"]` |
| 自动化 | ✅ `test_check_privacy_leak_id_card`(已存在) |
| 测试结果 | ✅ 通过 |
#### TC-204
| TC-ID | TC-204 |
|-------|--------|
| 用例名称 | 16 位银行卡号 → bank_card |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("银行卡6222021234567890")` |
| 预期结果 | 1. leaked == `["bank_card"]` |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-205
| TC-ID | TC-205 |
|-------|--------|
| 用例名称 | 个人邮箱(非公司域名)→ personal_email |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("联系方式 john.doe@gmail.com")` |
| 预期结果 | 1. leaked == `["personal_email"]` |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-206
| TC-ID | TC-206 |
|-------|--------|
| 用例名称 | 公司邮箱(应豁免)→ 空列表 |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("邮箱 zhang.san@servyou-it.com")`<br>2. `check_privacy_leak("邮箱 zhang.san@servyou.com.cn")` |
| 预期结果 | 1. 两次 leaked == `[]` |
| 业务说明 | 公司域名应豁免,不算隐私 |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-207
| TC-ID | TC-207 |
|-------|--------|
| 用例名称 | 多字段同时命中 |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("电话13800138000 身份证11010119900307123X")` |
| 预期结果 | 1. leaked == `["phone", "id_card"]`(顺序按检测顺序) |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-208
| TC-ID | TC-208 |
|-------|--------|
| 用例名称 | 正常文本 → 空列表 |
| 前置条件 | 无 |
| 测试步骤 | 1. `check_privacy_leak("这是正常的工作沟通内容")` |
| 预期结果 | 1. leaked == `[]` |
| 自动化 | ✅ `test_check_privacy_leak_clean_text_empty`(已存在) |
| 测试结果 | ✅ 通过 |
---
### 2.D 分类与建议
#### TC-301
| TC-ID | TC-301 |
|-------|--------|
| 用例名称 | 命中 profanity 分类 |
| 前置条件 | 命中任一自定义词 |
| 测试步骤 | 1. `moderate("你爱找谁找谁")`<br>2. 检查 category |
| 预期结果 | 1. category == `ModerationCategory.PROFANITY` |
| 自动化 | ❌ 待补(`_classify` 内部方法) |
| 测试结果 | ⚠️ 待测试 |
#### TC-302
| TC-ID | TC-302 |
|-------|--------|
| 用例名称 | profanity 分类的建议文案 |
| 前置条件 | category == PROFANITY |
| 测试步骤 | 1. 检查 suggestion 字段 |
| 预期结果 | 1. suggestion 含"建议改为更专业的表达" |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-303
| TC-ID | TC-303 |
|-------|--------|
| 用例名称 | politics 分类的建议文案(理论) |
| 前置条件 | 假设有 politics 词被命中(当前词库无) |
| 测试步骤 | 1. 直接调用 `_generate_suggestion(ModerationCategory.POLITICS, [])` |
| 预期结果 | 1. suggestion 含"避免讨论政治话题" |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-304
| TC-ID | TC-304 |
|-------|--------|
| 用例名称 | privacy 分类的建议文案(理论) |
| 前置条件 | 同 TC-303 |
| 测试步骤 | 1. `_generate_suggestion(ModerationCategory.PRIVACY, ["phone"])` |
| 预期结果 | 1. suggestion 含"请勿发送员工隐私信息" |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
---
### 2.E 边界与异常
#### TC-401
| TC-ID | TC-401 |
|-------|--------|
| 用例名称 | 超长文本(10000 字)性能可接受 |
| 前置条件 | 无 |
| 测试步骤 | 1. 构造 10000 字正常文本<br>2. `moderate(text)`<br>3. 计时 |
| 预期结果 | 1. 耗时 < 50ms<br>2. action == PASS |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-402
| TC-ID | TC-402 |
|-------|--------|
| 用例名称 | Unicode emoji 不误报 |
| 前置条件 | 无 |
| 测试步骤 | 1. `moderate("您好 😊 请问电脑怎么开?")` |
| 预期结果 | 1. action == PASS<br>2. matched_words == [] |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-403
| TC-ID | TC-403 |
|-------|--------|
| 用例名称 | 命中词被拆开("你爱找 谁找谁")不命中 |
| 前置条件 | 无 |
| 测试步骤 | 1. `moderate("你爱找 谁找谁")`(中间有空格) |
| 预期结果 | 1. action == PASSDFA 词库匹配需要连续字符) |
| 业务说明 | 词库设计缺陷:故意加空格绕过;但当前不视为关键风险 |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-404
| TC-ID | TC-404 |
|-------|--------|
| 用例名称 | wordfilter 库缺失时降级(异常路径) |
| 前置条件 | 1) 模拟 wordfilter 库导入失败 |
| 测试步骤 | 1. `pytest --mock wordfilter_import_failed`<br>2. 调用 `moderate(text)` |
| 预期结果 | 1. 不抛异常<br>2. 返回 PASS + 日志告警<br>3. 降级为仅隐私正则 |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
---
### 2.F 词库动态管理(v1.0 方法存在,路由未挂)
#### TC-501
| TC-ID | TC-501 |
|-------|--------|
| 用例名称 | add_custom_word 动态加词 |
| 前置条件 | 服务实例已初始化 |
| 测试步骤 | 1. `service.add_custom_word("测试词")`<br>2. `moderate("测试词")` |
| 预期结果 | 1. moderate 返回 WARN<br>2. matched_words 含"测试词" |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试 |
#### TC-502
| TC-ID | TC-502 |
|-------|--------|
| 用例名称 | remove_custom_word 动态删词(实际未生效) |
| 前置条件 | 词库含"测试词" |
| 测试步骤 | 1. `service.remove_custom_word("测试词")`<br>2. `moderate("测试词")` |
| 预期结果 | 1. wordfilter.remove 不支持,实际词仍命中<br>2. 行为为 WARN(已知缺陷) |
| 业务说明 | v1.0 限制:删词需重启服务 |
| 自动化 | ❌ 待补 |
| 测试结果 | ⚠️ 待测试(记录缺陷) |
---
### 2.G E2E 坐席端(待补)
#### TC-601
| TC-ID | TC-601 |
|-------|--------|
| 用例名称 | 坐席端发消息命中 WARN → 前端显示黄色提示条 |
| 前置条件 | 1) 坐席已登录<br>2) 会话已分配 |
| 测试步骤 | 1. 在聊天框输入"自己不会百度吗"<br>2. 点"发送" |
| 预期结果 | 1. 后端返回 action=WARN<br>2. 前端聊天框上方出现黄色提示条<br>3. 提示条显示"⚠️ 检测到敏感词:自己不会百度吗"<br>4. 消息仍可发送 |
| 自动化 | ❌ 阻塞:需等前端集成审核回调 |
| 测试结果 | 🚫 阻塞 |
#### TC-602
| TC-ID | TC-602 |
|-------|--------|
| 用例名称 | 坐席端发消息命中隐私 → 前端显示隐私警告 |
| 前置条件 | 1) 坐席已登录<br>2) `check_privacy_leak` 已接入主流程 |
| 测试步骤 | 1. 在聊天框输入"请联系我 13800138000"<br>2. 点"发送" |
| 预期结果 | 1. 后端检测到 phone<br>2. 前端显示隐私警告"请勿发送员工手机号" |
| 自动化 | ❌ 阻塞:v1.0 隐私检测未接入主流程 |
| 测试结果 | 🚫 阻塞 |
---
### 2.H 性能(v1.1 补)
#### TC-701
| TC-ID | TC-701 |
|-------|--------|
| 用例名称 | 1000 字文本审核耗时 < 10ms |
| 前置条件 | 无 |
| 测试步骤 | 1. 构造 1000 字文本<br>2. `moderate(text)` 1000 次取平均 |
| 预期结果 | 1. 平均耗时 < 10ms |
| 自动化 | ❌ v1.1 补 |
| 测试结果 | 🚫 阻塞 |
#### TC-702
| TC-ID | TC-702 |
|-------|--------|
| 用例名称 | 并发 100 QPS 不报错 |
| 前置条件 | 服务运行中 |
| 测试步骤 | 1. locust / wrk 模拟 100 QPS<br>2. 持续 60s |
| 预期结果 | 1. 错误率 < 0.1%<br>2. P99 延迟 < 50ms |
| 自动化 | ❌ v1.1 补 |
| 测试结果 | 🚫 阻塞 |
---
### 2.I 回归
#### TC-801
| TC-ID | TC-801 |
|-------|--------|
| 用例名称 | 完整 13 用例全跑通过 |
| 前置条件 | 无 |
| 测试步骤 | 1. `pytest tests/test_content_moderation.py -v` |
| 预期结果 | 1. 13 passed(基于决策保护后的状态) |
| 自动化 | ✅ 已有 |
| 测试结果 | ✅ 通过(部署后基线) |
---
## 三、缺陷登记
| 缺陷 | 严重度 | 状态 | 解决版本 |
|------|--------|------|----------|
| 隐私正则 `\b` 中文场景失效 | 🔴 严重 | ✅ 已修复 | v0.7.1 |
| 命中动作固定 WARN,无法升级 BLOCK | 🟠 高 | ⏸️ 决策保留 | v1.1 |
| 词库写死,未接 system_config | 🟡 中 | 🔴 未修复 | v1.1 |
| 后台词库管理 UI 缺失 | 🟡 中 | 🔴 未开发 | v1.1 |
| remove_custom_word 实际无效 | 🟢 低 | 🔴 已知缺陷 | v1.1(换库) |
| 词库命中无审计日志 | 🟡 中 | 🔴 未实现 | v1.1 |
---
## 四、测试通过标准
### 4.1 v1.0(已上线)
- [x] 13 个 pytest 用例全部通过(基于决策保护)
- [x] 看板验真测试报告 ⑤节 11/13 通过(2 失败均为已决策接受项)
- [x] 服务运行时无 ERROR 日志
### 4.2 v1.1 目标
- [ ] 31 用例 100% 通过(或已知缺陷明确登记)
- [ ] 隐私正则覆盖 6+ 类(含军官证/护照/车牌)
- [ ] 词库可数据库化运营
- [ ] 命中动作可配置
- [ ] E2E 坐席端提示闭环
---
## 五、关联文档
| 文档 | 位置 | 关联点 |
|------|------|--------|
| 关联 PRD | `01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md` | 需求来源 |
| 关联技术方案 | `02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md` | 实现细节 |
| 看板验真测试报告 | `03-测试文档/04-版本测试报告/看板验真-测试报告-20260707.md` | 历史基线 ⑤节 |
| 自动化测试源码 | `src/backend/tests/test_content_moderation.py` | 13 用例 |
| 内容审核服务源码 | `src/backend/app/services/content_moderation_service.py` | 实现 |
| 快速回复规则测试用例 | `03-测试文档/03-功能测试用例/TC-通用-002-快速回复规则后台管理.md` | 模板参考 |
---
## 六、变更日志
| 版本 | 日期 | 变更 | 变更人 |
|------|------|------|--------|
| v1.0 | 2026-07-28 | 首次整理:基于看板验真 13 用例扩展至 31 用例,补充 E2E/性能/边界场景 | 宋献 |