All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 46s
## 数据模型 - ChatSession +13 字段 (scopeType/scopeId/parentKnowledgeBaseId/createdFrom/isPinned/isArchived/isDeleted/modelMode/modelId/lastMessageAt) - ChatMessage +scopeSnapshot (消息级 scope 快照) - ChatCitation +lineStart/lineEnd +sourceId 索引 - 5 个新查询索引 ## 核心能力 - open-or-create: 同 scope 继续会话 (200) / 新建 (201) - scope 级检索: global/knowledge_base/material/knowledge_item/folder - listSessions: scope 过滤 + isDeleted 排除 + isPinned 排序 + 分页元数据 - 自动标题: 首条消息截取 + 词边界处理 - 软删除 + 置顶/归档 - scope 字段创建后不可修改 - 全部端点 userId 鉴权 ## 文档 - docs/chat-scope-design.md (设计文档 + 决策表) - docs/chat-scope-api-contract.md (API 契约) - docs/chat-scope-test-plan.md (33 条测试用例) - prisma/migrations/backfill_chat_scope.sql (旧数据回填) ## Bug 修复 - #104: KnowledgeItem.sourceRef 填充 (material scope 检索修复) - #102: sendMessageStream aiGateway null 保护 - listSessions isDeleted/isArchived 过滤 + 分页 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
114 lines
4.2 KiB
Markdown
114 lines
4.2 KiB
Markdown
# ChatScope 测试计划
|
||
|
||
> CHAT-702/703/704 | 2026-06-06
|
||
|
||
---
|
||
|
||
## 1. Scope 回归测试 (#89 CHAT-702)
|
||
|
||
### 1.1 创建会话 — open-or-create
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 1 | 新建 material scope 会话 | HTTP 201, scopeType=material, scopeId=sourceId |
|
||
| 2 | 相同 scope 再次调用 | HTTP 200, 返回同一个 session id |
|
||
| 3 | 不同 scopeId 调用 | HTTP 201, 创建新会话 |
|
||
| 4 | global scope 调用 | HTTP 201, scopeId=null, 每次都新建 |
|
||
| 5 | scopeType 不合法 | HTTP 400 |
|
||
| 6 | scopeType=material 但 scopeId 为空 | HTTP 400 |
|
||
|
||
### 1.2 会话列表 — scope 过滤
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 7 | scopeType + scopeId 精确过滤 | 只返回匹配的会话 |
|
||
| 8 | parentKnowledgeBaseId 过滤 | 返回该 KB 下所有会话 |
|
||
| 9 | 无过滤(全局列表) | 返回用户所有未删除会话 |
|
||
| 10 | isDeleted 会话不出现在列表 | 过滤掉 |
|
||
|
||
### 1.3 发送消息 — scope 快照
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 11 | 发消息后检查 scopeSnapshot | 用户消息和 AI 消息都有正确的 scopeSnapshot |
|
||
| 12 | 历史消息查询 | 每条消息都带 scopeSnapshot |
|
||
|
||
### 1.4 检索范围
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 13 | material scope: 只检索 sourceRef=scopeId 的内容 | 检索结果只来自该资料 |
|
||
| 14 | knowledge_item scope: 只检索 id=scopeId 的内容 | 检索结果只来自该知识点 |
|
||
| 15 | knowledge_base scope: 检索整个知识库 | 检索结果覆盖整个 KB |
|
||
| 16 | global scope: 不检索 | 返回空上下文 |
|
||
|
||
### 1.5 会话管理
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 17 | PATCH 更新 title | 成功更新 |
|
||
| 18 | PATCH 尝试修改 scopeType | 静默忽略,scopeType 不变 |
|
||
| 19 | PATCH 尝试修改 scopeId | 静默忽略,scopeId 不变 |
|
||
| 20 | DELETE 软删除 | isDeleted=true,消息保留 |
|
||
| 21 | 软删除后 open-or-create | 创建新会话,不返回已删除的 |
|
||
|
||
---
|
||
|
||
## 2. 防上下文污染测试 (#90 CHAT-703)
|
||
|
||
### 2.1 跨 scope 隔离
|
||
|
||
| # | 测试用例 | 步骤 | 预期 |
|
||
|---|---------|------|------|
|
||
| 22 | 资料 A 和资料 B 不串 | 1. 在 material/A 会话问"这篇文章讲了什么" 2. 切到 material/B 会话问"和之前那篇比呢" | AI 不知道 A 的内容 |
|
||
| 23 | 知识点和知识库不串 | 1. 在 knowledge_item/I 会话问 2. 切到 knowledge_base 会话继续问 | AI 应能检索整个 KB |
|
||
| 24 | 全局和绑定不串 | 1. 在 knowledge_base/K 会话问 2. 切到 global 会话问"刚才那个知识库里有什么" | AI 不知道之前的对话 |
|
||
|
||
### 2.2 并发会话
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 25 | 同一 scope 多会话(手动新对话创建多个) | 每个会话的消息互不干扰 |
|
||
| 26 | 快速切换会话发消息 | 每条消息写入正确的 sessionId |
|
||
|
||
---
|
||
|
||
## 3. 来源删除测试 (#91 CHAT-704)
|
||
|
||
### 3.1 知识库删除
|
||
|
||
| # | 测试用例 | 步骤 | 预期 |
|
||
|---|---------|------|------|
|
||
| 27 | 知识库被删除后 | 1. 创建 kb/K 的会话 2. 删除知识库 K 3. 打开会话 | 会话仍存在,需手动处理 |
|
||
| 28 | 知识库被删除后检索 | 同上,发消息 | 检索结果为空,提示知识库已删除 |
|
||
|
||
### 3.2 资料/知识点删除
|
||
|
||
| # | 测试用例 | 步骤 | 预期 |
|
||
|---|---------|------|------|
|
||
| 29 | 资料被删除后 | 1. 创建 material/M 的会话 2. 删除资料 M 3. 打开会话 | 会话仍然存在 |
|
||
| 30 | 资料被删除后检索 | 同上,发消息 | 检索不到被删资料的内容 |
|
||
| 31 | 知识点被删除后 | 1. 创建 knowledge_item/I 的会话 2. 删除知识点 I 3. 打开会话 | 会话仍然存在,标题不变 |
|
||
| 32 | 知识点被删除后检索 | 同上,发消息 | 检索结果为空 |
|
||
|
||
### 3.3 恢复
|
||
|
||
| # | 测试用例 | 预期 |
|
||
|---|---------|------|
|
||
| 33 | 删除资料后重新导入同名资料 | scopeId 不同,不会关联到旧会话 |
|
||
|
||
---
|
||
|
||
## 执行方式
|
||
|
||
```bash
|
||
# 后端启动后在本地执行
|
||
npx jest --config jest.config.ts --testPathPattern="rag-chat"
|
||
|
||
# 或手动 curl 测试
|
||
curl -X POST http://localhost:3000/rag-chat/sessions \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"scopeType":"material","scopeId":"test_source_id","createdFrom":"material_detail"}'
|
||
```
|