Files
qihuaiflow/tests/evolution-test-case-report.md
T
qyyfw 78ff261b5a 初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物)
- 后端 Python FastAPI
- 数据库迁移 001-015
- Docker部署配置
- 微信ClawBot API频道绑定
- 项目文档 README.md
2026-07-02 14:40:46 +08:00

635 lines
28 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.
# 测试用例 — AI自进化功能模块
## 1. 需求依据
- **PRD路径**:`/data/share/project/docs/PRD-self-evolution.md`
- **文档版本**:v1.0(2026-05-26)
- **功能概述**:Qihu-AIFlow平台AI自进化功能。每个用户的AI在对话中持续学习进化,前端通过「AI进化驾驶舱」展示进化过程。深度整合xSkill开源框架,实现对话轨迹→原子任务→技能聚类→技能生成→灰度验证的完整流水线。
### 需求侧功能模块(共6大模块)
| 编号 | 需求模块 | PRD章节 | 功能点数 |
|------|----------|---------|----------|
| R1 | 默认全局持续进化 | 7.2-功能一 | 5 |
| R2 | 对话自动分析(xSkill流水线) | 7.2-功能二 | 7 |
| R3 | 技能库管理 | 7.2-功能三 | 6 |
| R4 | AI进化驾驶舱(4个子面板) | 7.2-功能四 | 14 |
| R5 | 与AI聊天无缝衔接 | 7.2-功能五 | 5 |
| R6 | 数据隔离(用户级别) | 7.2-功能六 | 6 |
---
## 2. 代码功能模块概览
### 2.1 后端代码模块
| 模块 | 说明 | 主要代码位置 |
|------|------|-------------|
| 技能管理API | 技能CRUD、冻结/解冻、历史/轨迹查询 | `backend/python/app/api/evolution.py:33-139` |
| 进化事件API | 进化事件列表查询 | `backend/python/app/api/evolution.py:143-157` |
| 驾驶舱数据API | 技能树/时间线/统计/画像4面板数据 | `backend/python/app/api/evolution.py:160-202` |
| 轨迹提交API | 对话轨迹提交入口 | `backend/python/app/api/evolution.py:205-214` |
| EvolutionService | 核心业务逻辑层,12个公开方法 | `backend/python/app/services/evolution_service.py` |
| 数据模型(ORM) | 4张表定义 | `backend/python/app/models.py:741-792` |
| 路由注册 | prefix="/app/api/evolution" | `backend/python/main.py:140` |
### 2.2 前端代码模块
| 模块 | 说明 | 主要代码位置 |
|------|------|-------------|
| AI进化驾驶舱页面 | 主页面组件(4面板切换+数据加载) | `frontend/src/pages/front/EvolutionDashboard.tsx` (631行) |
| 驾驶舱样式 | 完整CSS样式 | `frontend/src/pages/front/EvolutionDashboard.css` (1019行) |
| 技能图谱面板 | Panel A:按分类分组展示技能节点 | `EvolutionDashboard.tsx:48-152` |
| 进化时间线面板 | Panel B:垂直时间线+事件筛选 | `EvolutionDashboard.tsx:155-201` |
| 技能使用统计面板 | Panel C:排行+趋势图+饼图+提醒 | `EvolutionDashboard.tsx:205-340` |
| AI能力画像面板 | Panel D:等级环+雷达图+标签云+建议 | `EvolutionDashboard.tsx:344-500` |
| evolutionAPI | 前端API调用定义(15个方法) | `frontend/src/api/index.ts:415-455` |
| 类型定义 | TypeScript interfaces | `frontend/src/types/index.ts:381-478` |
| 路由配置 | `#/evolution` | `frontend/src/App.tsx:98` |
| 导航入口 | 侧边栏"AI进化" | `frontend/src/components/layout/FrontLayout.tsx:14` |
---
## 3. 需求与代码差异说明
### 3.1 PRD中未在代码中体现的内容
| 编号 | PRD功能点 | 状态 | 说明 |
|------|-----------|------|------|
| GAP-01 | **技能回滚**:POST /skills/{name}/rollback/{version} | ❌ 未实现 | PRD明确要求回滚到历史版本,但API中无此端点,前端无回滚UI |
| GAP-02 | **对话溯源跳转**:点击来源对话可跳转到原始对话记录 | ❌ 未实现 | PRD要求"点击可跳转到原始对话记录",前端技能详情弹窗中有来源轨迹按钮但仅调用API(`evolutionAPI.getSkillTrajectories`),后端 `get_skill_trajectories` 返回的是用户最近20条轨迹而非该技能的关联轨迹 |
| GAP-03 | **新技能萌芽动画** | ❌ 未实现 | PRD面板A中要求"新技能生成时,技能树上有「萌芽」动画效果",前端代码中未找到相关CSS动画 |
| GAP-04 | **时间线导出** | ❌ 未实现 | PRD面板B要求"支持将进化时间线导出为图片或PDF分享",前端代码中无导出功能 |
| GAP-05 | **画像分享卡片** | ❌ 未实现 | PRD面板D要求"支持将AI能力画像生成为分享卡片",前端代码中无此功能 |
| GAP-06 | **Skill加载提示标签** | ❌ 未实现 | PRD功能五要求"在消息下方显示小标签:🚀 使用了你的专属技能:XXX",代码中未见此实现 |
| GAP-07 | **对话结束钩子(自动提交轨迹)** | ❌ 未自动触发 | PRD要求对话结束时自动提交轨迹,但当前仅提供手动API入口 `/trajectory/submit`,前端ChatPage中未见对话结束自动调用的钩子 |
| GAP-08 | **Watcher线程管理** | ❌ 未实现 | PRD要求用户注册后自动启动watcher线程持续进化,`UserEvolutionConfig`模型已定义但服务层无读写该表的代码,watcher状态管理未实现 |
| GAP-09 | **xSkill管线自动调度** | ❌ 未实现 | PRD要求轨迹进入xSkill流水线自动处理,但后端仅实现轨迹落盘(status=discovered),后续状态流转(splitting→...→done)依赖外部xSkill进程 |
| GAP-10 | **统计图表的交互联动** | ❌ 未实现 | PRD面板C要求"点击统计图表中的任意项可跳转到对应技能详情",前端StatisticsPanel中无此交互 |
### 3.2 PRD中未覆盖但代码中存在的功能模块
| 编号 | 代码模块 | 位置 | 说明 | 建议产品确认 |
|------|----------|------|------|-------------|
| EXTRA-01 | **evolutionAPI.submitTrajectory** 提供独立的手动轨迹提交入口 | `api/index.ts:453` | 前端API层暴露了轨迹提交方法,但在当前驾驶舱页面中未被任何组件调用 | 是否需要在驾驶舱中增加手动提交轨迹的入口? |
| EXTRA-02 | **能力维度雷达图使用内联SVG实现** | `EvolutionDashboard.tsx:398-466` | 前端用内联SVG而非第三方图表库绘制雷达图,包含5层参考多边形、轴线、数据多边形 | 技术决策,无需产品确认 |
---
## 4. 测试用例
### 4.1 技能管理 API
#### TC-SKILL-01 [P0] 验证获取当前用户技能列表
- **前置条件**:用户已登录;数据库中存在该用户的技能数据
- **步骤**:
[1] 使用有效JWT Token调用 `GET /app/api/evolution/skills`
[2] 检查响应状态码和结构
- **预期结果**:
- HTTP 200,响应包含 `code: 0`
- `data` 为数组,每项包含 `id, skill_name, description, tags, category, state, use_count, version, created_at, updated_at`
- 仅返回当前用户的技能数据,不包含其他用户的技能
- **备注**:默认按 `updated_at` 降序排列
#### TC-SKILL-02 [P0] 验证按状态过滤技能
- **前置条件**:数据库中存在不同状态的技能
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills?state=baby`
[2] 调用 `GET /app/api/evolution/skills?state=main`
[3] 调用 `GET /app/api/evolution/skills?state=frozen`
[4] 调用 `GET /app/api/evolution/skills?state=staging`
- **预期结果**:
- 每次返回的技能 `state` 字段均匹配查询参数
- 传入无效状态值时系统行为一致(返回空列表或400错误)
- **备注**:P0 覆盖核心过滤逻辑
#### TC-SKILL-03 [P0] 验证按关键词搜索技能
- **前置条件**:数据库中存在技能名包含"python"和"写作"的技能
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills?keyword=python`
[2] 调用 `GET /app/api/evolution/skills?keyword=写作`
[3] 调用 `GET /app/api/evolution/skills?keyword=不存在的关键词`
- **预期结果**:
- [1][2] 返回 `skill_name` 包含关键词的技能
- [3] 返回空列表
- **备注**:模糊匹配
#### TC-SKILL-04 [P1] 验证技能列表排序功能
- **前置条件**:数据库中存在多条技能记录
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills?sort_by=use_count&order=desc`
[2] 调用 `GET /app/api/evolution/skills?sort_by=created_at&order=asc`
[3] 调用 `GET /app/api/evolution/skills?sort_by=invalid_field`
- **预期结果**:
- [1] 按使用次数降序排列
- [2] 按创建时间升序排列
- [3] 返回错误或默认排序回退
#### TC-SKILL-05 [P0] 验证获取技能详情
- **前置条件**:数据库中存在技能名为"fix_python_bug"的技能
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills/fix_python_bug`
[2] 调用 `GET /app/api/evolution/skills/不存在的技能名`
- **预期结果**:
- [1] HTTP 200,返回完整技能信息(含 `skill_md_content` 字段)
- [2] HTTP 404,提示技能不存在
- **备注**:如果 `skill_md_path` 文件存在,`skill_md_content` 应包含文件内容
#### TC-SKILL-06 [P0] 验证编辑技能元数据
- **前置条件**:存在可编辑的技能
- **步骤**:
[1] 调用 `PUT /app/api/evolution/skills/{skill_name}`,body: `{"description": "新描述", "tags": ["新标签"], "category": "新分类"}`
[2] 再次获取该技能详情,检查变更
[3] 检查进化事件表中是否记录了编辑事件
- **预期结果**:
- [1] HTTP 200,返回更新后的技能对象,`version` 字段 +1
- [2] 技能详情反映变更
- [3] `evolution_events` 表中新增一条 event_type="edited" 的记录,含 version_before/version_after
- **备注**:仅可编辑 description, tags, category
#### TC-SKILL-07 [P0] 验证删除技能
- **前置条件**:存在可删除的技能
- **步骤**:
[1] 记录删除前的技能列表总数 N
[2] 调用 `DELETE /app/api/evolution/skills/{skill_name}`
[3] 调用 `GET /app/api/evolution/skills` 检查技能列表
[4] 调用 `GET /app/api/evolution/skills/{skill_name}` 确认已不存在
[5] 检查进化事件表中是否记录了删除事件
- **预期结果**:
- [2] HTTP 200
- [3] 技能列表总数为 N-1
- [4] HTTP 404
- [5] `evolution_events` 表中新增 event_type="deleted" 的记录
- **备注**:删除后事件表仍保留历史记录
#### TC-SKILL-08 [P0] 验证冻结技能
- **前置条件**:存在一个 state 为 "main" 的技能
- **步骤**:
[1] 调用 `POST /app/api/evolution/skills/{skill_name}/freeze`
[2] 调用 `GET /app/api/evolution/skills/{skill_name}` 确认状态
[3] 对已冻结的技能再次调用 freeze
- **预期结果**:
- [1] HTTP 200,state 变为 "frozen"
- [2] state 为 "frozen"
- [3] 幂等操作,不报错(state 保持 "frozen")
- 事件表中新增 event_type="frozen" 的记录
#### TC-SKILL-09 [P0] 验证解冻技能
- **前置条件**:存在一个 state 为 "frozen" 的技能
- **步骤**:
[1] 调用 `POST /app/api/evolution/skills/{skill_name}/unfreeze`
[2] 调用 `GET /app/api/evolution/skills/{skill_name}` 确认状态
- **预期结果**:
- [1] HTTP 200,state 变为 "main"
- [2] state 为 "main"
- 事件表中新增 event_type="unfrozen" 的记录
#### TC-SKILL-10 [P1] 验证技能版本历史查询
- **前置条件**:某技能经历多次编辑/冻结/解冻操作
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills/{skill_name}/history`
[2] 检查返回的事件列表
- **预期结果**:
- HTTP 200,返回按照 `created_at` 排序的事件列表
- 每项包含 event_type, description, version_before, version_after, created_at
- 支持未操作过的技能(返回空数组)
#### TC-SKILL-11 [P1] 验证技能来源轨迹查询
- **前置条件**:存在技能和对应的轨迹数据
- **步骤**:
[1] 调用 `GET /app/api/evolution/skills/{skill_name}/trajectories`
- **预期结果**:
- HTTP 200
- 返回该技能的关联轨迹列表(最多20条)
- **备注**:后端 `get_skill_trajectories` 当前仅按 user_id 过滤,未按 skill_name 过滤——此为缺陷,已记录在 3.1 GAP-02
---
### 4.2 进化事件 API
#### TC-EVENT-01 [P0] 验证获取进化事件列表
- **前置条件**:数据库中已有多条进化事件记录
- **步骤**:
[1] 调用 `GET /app/api/evolution/events`
[2] 检查返回的事件列表
- **预期结果**:
- HTTP 200,最多返回50条(默认 limit)
- 每项包含 id, skill_name, event_type, description, version_before, version_after, created_at
- 仅返回当前用户的进化事件
#### TC-EVENT-02 [P1] 验证按事件类型筛选
- **前置条件**:数据库中有不同类型的进化事件
- **步骤**:
[1] 调用 `GET /app/api/evolution/events?event_type=created`
[2] 调用 `GET /app/api/evolution/events?event_type=frozen`
[3] 调用 `GET /app/api/evolution/events?event_type=edited`
- **预期结果**:每次返回的事件类型均匹配筛选条件
#### TC-EVENT-03 [P1] 验证按技能名筛选事件
- **前置条件**:某技能有多个关联事件
- **步骤**:
[1] 调用 `GET /app/api/evolution/events?skill_name={skill_name}`
- **预期结果**:仅返回该技能相关的进化事件
#### TC-EVENT-04 [P2] 验证limit参数边界
- **前置条件**:数据库中有大量事件记录
- **步骤**:
[1] 调用 `GET /app/api/evolution/events?limit=1`
[2] 调用 `GET /app/api/evolution/events?limit=200`
[3] 调用 `GET /app/api/evolution/events?limit=201`
[4] 调用 `GET /app/api/evolution/events?limit=0`
- **预期结果**:
- [1] 返回1条
- [2] 返回最多200条
- [3] 返回错误或限制到200
- [4] 返回错误或默认50条
---
### 4.3 驾驶舱面板 API
#### TC-DASH-01 [P0] 验证技能图谱数据 (dashboard/skill-tree)
- **前置条件**:用户拥有多个分类的技能
- **步骤**:
[1] 调用 `GET /app/api/evolution/dashboard/skill-tree`
[2] 检查返回结构
- **预期结果**:
- HTTP 200
- `data.categories` 为数组,每组包含 `name` 和 `skills[]`
- `data` 包含统计字段:`total_count, main_count, baby_count, staging_count, frozen_count`
- 技能数据仅限当前用户
#### TC-DASH-02 [P0] 验证进化时间线数据 (dashboard/timeline)
- **前置条件**:用户有进化事件记录
- **步骤**:
[1] 调用 `GET /app/api/evolution/dashboard/timeline`
[2] 检查返回数据
- **预期结果**:
- HTTP 200
- 返回事件列表,按 created_at 降序
- 支持 `event_type` 筛选参数
- 无事件时返回空数组
#### TC-DASH-03 [P0] 验证技能使用统计 (dashboard/statistics)
- **前置条件**:用户有使用统计的历史数据
- **步骤**:
[1] 调用 `GET /app/api/evolution/dashboard/statistics`
[2] 检查返回数据结构
- **预期结果**:
- HTTP 200
- `data` 包含字段:`ranking[]`(TOP N排行)、`total_use_count`、`category_distribution[]`、`daily_trend[]`、`low_use_skills[]`
- `ranking` 中每项含 `skill_name, category, use_count, state, last_used_at`
- `daily_trend` 数据点含 `date, count`
#### TC-DASH-04 [P0] 验证AI能力画像 (dashboard/profile)
- **前置条件**:用户有足够的技能数据支撑画像计算
- **步骤**:
[1] 调用 `GET /app/api/evolution/dashboard/profile`
[2] 检查返回结构
- **预期结果**:
- HTTP 200
- `data` 包含字段:`level`(1-10的等级)、`level_title`(等级称号)、`total_skills`、`active_skills`、`staging_skills`、`baby_skills`、`total_use_count`
- `data.dimensions` 为数组,每项含 `label, value, maxValue`
- `data.tags` 为能力标签数组
- `data.growth_suggestions` 为成长建议数组
#### TC-DASH-05 [P1] 验证驾驶舱数据空态响应
- **前置条件**:用户为新用户,无任何进化数据
- **步骤**:
[1] 分别调用4个驾驶舱API端点
[2] 检查响应
- **预期结果**:均返回 HTTP 200,数据字段为空数组或零值,不报错
---
### 4.4 轨迹提交 API
#### TC-TRAJ-01 [P0] 验证提交对话轨迹
- **前置条件**:用户已登录
- **步骤**:
[1] 调用 `POST /app/api/evolution/trajectory/submit`
body: `{"conversation_id": "conv-001", "messages": [{"role": "user", "content": "帮我写一个Python函数"}, {"role": "assistant", "content": "好的..."}]}`
[2] 检查响应
[3] 检查数据库 `evolution_trajectories` 表中新增记录
[4] 检查文件系统中轨迹文件是否生成
- **预期结果**:
- [1] HTTP 200,`message` 为"轨迹已提交"
- [2] `data` 包含 id, conversation_id, file_path, status="discovered", atom_count=0
- [3] 数据库中有新记录,`user_id` 为当前用户
- [4] 轨迹文件存在于 `/data/share/project/data/users/{user_id}/trajectories/traj_{id}/traj_{id}.md`
#### TC-TRAJ-02 [P1] 验证提交轨迹消息格式兼容性
- **前置条件**:用户已登录
- **步骤**:
[1] 提交包含多条轮次对话的轨迹
[2] 读取生成的轨迹文件内容
- **预期结果**:
- 轨迹文件格式为xSkill兼容的Markdown:`# Conversation Trajectory` + `## User` / `## Assistant` 交替分段
- `token_used` 正确累计所有消息的 tokens 字段
#### TC-TRAJ-03 [P1] 验证轨迹提交数据隔离
- **前置条件**:两个不同用户(userA 和 userB)
- **步骤**:
[1] userA 提交一个轨迹
[2] userB 调用 `GET /app/api/evolution/events` 查询事件
[3] userB 调用 `GET /app/api/evolution/skills` 查询技能
- **预期结果**:
- [2] userB 看不到 userA 的轨迹数据
- [3] userB 的技能数据不包含 userA 的技能
#### TC-TRAJ-04 [P2] 验证提交空消息列表
- **前置条件**:用户已登录
- **步骤**:
[1] 调用 `POST /app/api/evolution/trajectory/submit`,body: `{"conversation_id": "conv-empty", "messages": []}`
- **预期结果**:
- 返回成功(或合理的错误提示"消息列表不能为空")
- 轨迹状态正确记录
---
### 4.5 数据隔离
#### TC-ISOL-01 [P0] 验证用户A不能访问用户B的技能
- **前置条件**:userA 和 userB 各自有技能数据
- **步骤**:
[1] 使用 userA 的Token调用 `GET /app/api/evolution/skills`
[2] 使用 userB 的Token调用 `GET /app/api/evolution/skills`
[3] 使用 userA 的Token调用 `GET /app/api/evolution/skills/{userB的skill_name}`
- **预期结果**:
- [1] 仅返回 userA 的技能
- [2] 仅返回 userB 的技能
- [3] HTTP 404,userA 无法通过猜解技能名获取 userB 的数据
#### TC-ISOL-02 [P0] 验证用户A不能操作用户B的技能
- **前置条件**:userA 和 userB 各自有技能
- **步骤**:
[1] 使用 userA 的Token调用 `PUT /app/api/evolution/skills/{userB的skill_name}` 尝试编辑
[2] 使用 userA 的Token调用 `DELETE /app/api/evolution/skills/{userB的skill_name}` 尝试删除
[3] 使用 userA 的Token调用 `POST /app/api/evolution/skills/{userB的skill_name}/freeze` 尝试冻结
- **预期结果**:所有操作返回 HTTP 404(技能不存在),不修改 userB 的数据
#### TC-ISOL-03 [P1] 验证用户A不能查看用户B的进化事件
- **前置条件**:两个用户都有进化事件
- **步骤**:
[1] 使用 userA 的Token调用 `GET /app/api/evolution/events`
[2] 检查返回列表中是否出现 userB 的技能名
- **预期结果**:不包含任何 userB 相关的事件
#### TC-ISOL-04 [P1] 验证用户A不能查看用户B的驾驶舱数据
- **前置条件**:两个用户都有进化数据
- **步骤**:
[1] 使用 userA 的Token分别调用4个驾驶舱API
[2] 检查返回数据中是否包含 userB 的技能/事件信息
- **预期结果**:所有驾驶舱API仅返回当前用户的数据
---
### 4.6 前端—AI进化驾驶舱页面
#### TC-FE-01 [P0] 验证驾驶舱页面加载
- **前置条件**:用户已登录(admin账号)
- **步骤**:
[1] 导航到 `#/evolution`
[2] 观察页面加载过程
[3] 检查页面标题和统计摘要
- **预期结果**:
- 页面正常渲染,显示"AI进化驾驶舱"标题
- 加载过程中显示 loading spinner + "正在加载进化数据..." 文字
- 加载完成后显示统计数字(技能总数/已成熟/萌芽中)
- Console 无JS错误
#### TC-FE-02 [P0] 验证技能图谱面板(Panel A)
- **前置条件**:用户有技能数据
- **步骤**:
[1] 切换到"技能图谱"面板
[2] 检查技能节点展示
[3] 鼠标悬停在技能节点上
[4] 点击某个技能节点
- **预期结果**:
- 技能按 category 分组展示,不同状态用不同颜色标识(baby=粉色/main=绿色/staging=橙色/frozen=灰色)
- hover 显示 tooltip(技能名+描述+版本+更新时间)
- 点击弹出详情弹窗(名称/状态/版本/使用次数/分类/标签/操作按钮)
- 底部有状态色标图例
- 无技能时显示空态
#### TC-FE-03 [P0] 验证进化时间线面板(Panel B)
- **前置条件**:用户有进化事件数据
- **步骤**:
[1] 切换到"进化时间线"面板
[2] 检查时间线布局
[3] 点击不同的筛选按钮(全部/诞生/晋升/里程碑/更新/冻结/退回)
[4] 检查事件卡片内容
- **预期结果**:
- 垂直时间线展示,左侧圆点+连接线,右侧卡片
- 卡片显示:事件类型+技能名+描述+时间戳
- 筛选按钮正常工作,筛选结果正确
- 无事件时显示空态
#### TC-FE-04 [P0] 验证技能使用统计面板(Panel C)
- **前置条件**:用户有使用统计数据
- **步骤**:
[1] 切换到"技能使用统计"面板
[2] 检查技能使用排行
[3] 检查调用趋势图
[4] 检查类别分布饼图
[5] 检查低使用技能提醒(如有)
- **预期结果**:
- 排行条形图正常渲染,Top 3 带紫色渐变标记
- 趋势柱状图显示 Top 4 技能
- 饼图中心显示总调用次数,图例显示各分类占比
- 超过30天未使用的技能显示黄色警告提醒
- 无数据时显示"暂无数据"
- Panel切换时按需加载统计数据
#### TC-FE-05 [P0] 验证AI能力画像面板(Panel D)
- **前置条件**:用户有足够的技能数据
- **步骤**:
[1] 切换到"AI能力画像"面板
[2] 检查等级卡片
[3] 检查能力雷达图
[4] 检查能力标签云
[5] 检查成长建议
- **预期结果**:
- 等级卡片显示环形进度条(level/10)+ 等级数字 + 称号 + 四项统计
- 雷达图显示能力维度(文字创作/数据分析/代码能力/知识问答/流程自动化/创意生成等)
- 标签云有4级尺寸和6种颜色循环
- 成长建议列表以黄色星标开头
- 无画像数据时显示空态
#### TC-FE-06 [P1] 验证面板切换
- **前置条件**:页面已加载
- **步骤**:
[1] 依次点击4个面板切换按钮
[2] 来回切换 panels
[3] 切换过程中检查加载状态
- **预期结果**:
- 面板切换流畅,当前面板高亮
- statistics 和 profile 面板首次进入时显示"加载中..."
- 返回已加载过的面板不重新请求数据
#### TC-FE-07 [P1] 验证错误状态和重试
- **前置条件**:页面已加载
- **步骤**:
[1] 模拟后端API返回错误(或断开网络)
[2] 观察页面反应
[3] 点击"重试"按钮
- **预期结果**:
- 页面显示错误消息
- "重试"按钮可见且可用
- 点击"重试"重新发起API请求
#### TC-FE-08 [P1] 验证未登录状态
- **前置条件**:用户未登录
- **步骤**:
[1] 清除 localStorage 中的 token
[2] 导航到 `#/evolution`
- **预期结果**:自动重定向到 `#/login`
#### TC-FE-09 [P2] 验证CSS样式完整性
- **前置条件**:页面正常加载
- **步骤**:
[1] 检查所有面板的CSS类名是否存在
[2] 检查loading spinner动画是否流畅
[3] 检查响应式布局(不同分辨率下)
- **预期结果**:
- 所有CSS类在 `EvolutionDashboard.css` 中有定义
- 旋转动画正常工作
- 各分辨率下布局正常
---
### 4.7 前端—技能管理UI(技能详情弹窗中的操作)
#### TC-FE-MGR-01 [P1] 验证技能详情弹窗中显示操作按钮
- **前置条件**:在技能图谱面板中点击某技能节点
- **步骤**:
[1] 检查弹窗内容
[2] 确认操作按钮可见
- **预期结果**:
- 弹窗显示技能完整信息
- 包含编辑/删除/冻结(或解冻)等操作按钮
- 操作按钮根据技能状态正确切换(已冻结显示"解冻",否则显示"冻结")
#### TC-FE-MGR-02 [P1] 验证前端调用冻结API
- **前置条件**:弹窗中显示"冻结"按钮
- **步骤**:
[1] 点击"冻结"按钮
[2] 观察反馈
[3] 刷新技能列表确认状态变化
- **预期结果**:
- API调用 `/app/api/evolution/skills/{name}/freeze` 成功
- 技能状态变为"frozen"
- 前端显示操作成功提示
#### TC-FE-MGR-03 [P1] 验证前端调用删除API
- **前置条件**:弹窗中显示"删除"按钮
- **步骤**:
[1] 点击"删除"按钮
[2] 确认删除(如有确认弹窗)
[3] 观察技能列表变化
- **预期结果**:
- API调用 `DELETE /app/api/evolution/skills/{name}` 成功
- 技能从列表中移除
- 前端显示删除成功提示
---
### 4.8 数据库表结构验证
#### TC-DB-01 [P0] 验证 evolution_skills 表结构
- **前置条件**:数据库连接正常
- **步骤**:
[1] 查询表结构
[2] 验证所有字段存在且类型正确
- **预期结果**:表包含字段: id(PK), user_id(FK+INDEX), skill_name, description, tags(JSON), category, state, use_count, version, skill_md_path, git_commit_sha, last_used_at, created_at, updated_at
#### TC-DB-02 [P0] 验证 evolution_events 表结构
- **前置条件**:数据库连接正常
- **步骤**:
[1] 查询表结构
- **预期结果**:表包含字段: id(PK), user_id(FK+INDEX), skill_name, event_type, description, version_before, version_after, created_at
#### TC-DB-03 [P0] 验证 evolution_trajectories 表结构
- **前置条件**:数据库连接正常
- **步骤**:
[1] 查询表结构
- **预期结果**:表包含字段: id(PK), user_id(FK+INDEX), conversation_id, file_path, status, atom_count, token_used, created_at, processed_at
#### TC-DB-04 [P0] 验证 user_evolution_config 表结构
- **前置条件**:数据库连接正常
- **步骤**:
[1] 查询表结构
- **预期结果**:表包含字段: id(PK), user_id(FK+UNIQUE+INDEX), skill_dir_path, trajectory_dir_path, watcher_status, created_at, updated_at
---
### 4.9 技能状态流转验证
#### TC-STATE-01 [P0] 验证技能状态 baby → main
- **前置条件**:存在 state="baby" 的技能
- **步骤**:
[1] 通过数据层直接更新该技能 state="main"
[2] 调用 `GET /app/api/evolution/skills/{skill_name}` 确认状态
- **预期结果**:状态成功变更为 main
#### TC-STATE-02 [P0] 验证技能状态 main → staging → main
- **前置条件**:存在 state="main" 的技能
- **步骤**:
[1] 更新 state="staging"
[2] 查询确认
[3] 更新 state="main"
[4] 查询确认
- **预期结果**:状态变更成功
#### TC-STATE-03 [P0] 验证技能状态 main → frozen → main
- **前置条件**:存在 state="main" 的技能
- **步骤**:
[1] 调用 freeze API
[2] 确认 state="frozen"
[3] 调用 unfreeze API
[4] 确认 state="main"
- **预期结果**:完整走通 freeze→unfreeze 循环,事件表记录完整
---
## 5. 测试用例统计
| 类别 | P0 | P1 | P2 | 合计 |
|------|----|----|----|------|
| 技能管理 API | 7 | 2 | 0 | 9 |
| 进化事件 API | 1 | 2 | 1 | 4 |
| 驾驶舱面板 API | 4 | 1 | 0 | 5 |
| 轨迹提交 API | 1 | 2 | 1 | 4 |
| 数据隔离 | 2 | 2 | 0 | 4 |
| 前端驾驶舱页面 | 4 | 3 | 1 | 8 |
| 前端技能管理UI | 0 | 3 | 0 | 3 |
| 数据库表结构 | 4 | 0 | 0 | 4 |
| 技能状态流转 | 3 | 0 | 0 | 3 |
| **合计** | **26** | **15** | **3** | **44** |
---
## 6. PRD未覆盖的代码功能模块(需产品确认)
| 编号 | 模块 | 代码位置 | 建议确认项 |
|------|------|----------|-----------|
| EXTRA-01 | 独立轨迹提交API | `api/index.ts:453` | 驾驶舱页面是否需要手动轨迹提交入口? |
| EXTRA-02 | SVG内联雷达图 | `EvolutionDashboard.tsx:398-466` | 技术决策,无需产品确认 |
---
## 7. 测试环境要求
- **后端服务**:Python FastAPI 服务运行于 localhost:18080
- **前端服务**:React 应用通过 Nginx 代理运行于 8080 端口
- **数据库**:MySQL 8 (localhost:3306, root/pwd2026)
- **认证方式**:JWT Bearer Token,通过 POST /app/api/auth/login 获取
- **测试用户**:admin (管理员), demo_user (普通用户)
---
> **文档生成时间**:2026-05-29
> **文档版本**:v1.0
> **基于代码版本**:evolution.py (215行) + evolution_service.py (473行) + EvolutionDashboard.tsx (631行)
> **PRD版本**:v1.0 (2026-05-26)