Clarify product-level server and desktop commands while moving backend-only maintenance tasks into backend/Makefile. This keeps root automation focused on core flows and aligns the main OpenSpec specs with the new command boundaries.
11 KiB
11 KiB
Test Coverage
Purpose
定义测试覆盖规范,建立后端和前端的单元测试、集成测试及 E2E 测试体系,确保核心业务逻辑的测试覆盖率达到目标水平。
Requirements
Requirement: 建立单元测试体系
系统 SHALL 建立完整的单元测试体系,覆盖核心业务逻辑。
Scenario: config 包测试覆盖
- WHEN 运行 config 包的单元测试
- THEN SHALL 覆盖 Provider、Model、Stats 的 CRUD 操作
- THEN SHALL 测试正常场景和错误场景
- THEN SHALL 验证数据库操作的准确性
Scenario: router 包测试覆盖
- WHEN 运行 router 包的单元测试
- THEN SHALL 覆盖模型路由逻辑
- THEN SHALL 测试模型不存在、模型禁用、供应商禁用等场景
- THEN SHALL 验证路由结果的正确性
Scenario: protocol 包测试覆盖
- WHEN 运行 protocol 包的单元测试
- THEN SHALL 覆盖 OpenAI 和 Anthropic 协议转换逻辑
- THEN SHALL 测试请求转换、响应转换、流式转换
- THEN SHALL 验证转换的准确性和完整性
Scenario: LoadConfigFromPath 默认值验证
- WHEN 运行 config 加载管道的集成测试
- THEN SHALL 验证 LoadConfigFromPath 正确加载默认值
- THEN SHALL 验证 YAML 配置文件正确读取
- THEN SHALL 验证优先级链:CLI 参数 > 环境变量 > YAML 文件 > 默认值
- THEN SHALL 验证首次启动自动创建配置文件
- THEN SHALL 验证 SaveConfig 后重新 LoadConfig 数据一致
Scenario: 环境变量覆盖验证
- WHEN 设置
NEX_SERVER_PORT=9000和NEX_LOG_LEVEL=debug - THEN SHALL 成功加载
- THEN 配置值 SHALL 反映环境变量覆盖
Scenario: 自动创建配置文件验证
- WHEN 调用
LoadConfigFromPath并指向不存在的文件路径 - THEN SHALL 成功加载(不返回
missing configuration for 'configPath'错误) - THEN SHALL 返回默认配置对象
Scenario: handler 错误分支测试
- WHEN 运行 handler 层的单元测试
- THEN SHALL 覆盖 ModelHandler.CreateModel 的所有错误分支:ErrProviderNotFound(400)、ErrDuplicateModel(409)、通用错误(500)
- THEN SHALL 覆盖 ProviderHandler.UpdateProvider 的所有错误分支:ErrRecordNotFound(404)、ErrImmutableField(400)、通用错误(500)
- THEN SHALL 验证每个错误分支返回正确的 HTTP 状态码和错误响应格式
Scenario: service 聚合逻辑测试
- WHEN 运行 service 层的单元测试
- THEN SHALL 覆盖 StatsService.Aggregate 的所有分组模式:byProvider、byModel、byDate
- THEN SHALL 验证 aggregateByModel 正确拼接和拆分 providerID/modelName key
- THEN SHALL 验证 aggregateByDate 正确格式化日期并聚合
- THEN SHALL 覆盖空结果集、同名模型不同 provider 等边界场景
Scenario: provider service 工具方法测试
- WHEN 运行 provider service 的单元测试
- THEN SHALL 验证 isUniqueConstraintError 正确识别 SQLite 唯一约束冲突错误消息
- THEN SHALL 验证 List 方法对每个 provider 调用 MaskAPIKey
Scenario: engine 降级路径测试
- WHEN 运行 conversion engine 的单元测试
- THEN SHALL 验证 convertEmbeddingBody 在 decode 失败时返回原始 body(passthrough)
- THEN SHALL 验证 convertRerankBody 在 decode 失败时返回原始 body(passthrough)
- THEN SHALL 验证降级过程不 panic、不返回空 body
Scenario: openai decoder 边界场景测试
- WHEN 运行 openai decoder 的单元测试
- THEN SHALL 覆盖 assistant message content 为 JSON 数组格式的解析(text/refusal 类型)
- THEN SHALL 验证 decodeContentParts 正确提取文本内容和拒绝消息
Scenario: 业务逻辑负面测试
- WHEN 运行业务逻辑负面测试
- THEN SHALL 覆盖 JSON 格式错误请求体的处理
- THEN SHALL 覆盖并发创建相同 provider + model 的重复检测
- THEN SHALL 覆盖空结果集查询的正确处理
Requirement: 建立集成测试体系
系统 SHALL 建立集成测试体系,覆盖 API 端到端流程。
Scenario: OpenAI 协议集成测试
- WHEN 运行 OpenAI 协议的集成测试
- THEN SHALL 测试完整的请求-响应流程
- THEN SHALL 测试流式响应流程
- THEN SHALL 测试错误处理流程
Scenario: Anthropic 协议集成测试
- WHEN 运行 Anthropic 协议的集成测试
- THEN SHALL 测试完整的请求-响应流程
- THEN SHALL 测试流式响应流程
- THEN SHALL 测试协议转换的准确性
Scenario: 管理接口集成测试
- WHEN 运行管理接口的集成测试
- THEN SHALL 测试 Provider、Model、Stats 的 CRUD 操作
- THEN SHALL 验证 API 响应格式
- THEN SHALL 测试错误场景
Requirement: 提供测试工具函数
系统 SHALL 提供测试工具函数,简化测试编写。
Scenario: 测试数据库初始化
- WHEN 编写需要数据库的测试
- THEN SHALL 提供统一的测试数据库初始化函数
SetupTestDB - THEN SHALL 统一使用 SQLite
:memory:+MaxOpenConns(1)策略 - THEN SHALL 在测试结束后自动清理
- THEN 所有测试包 SHALL 通过
tests.SetupTestDB()获取测试数据库,不允许各自独立实现
Scenario: Mock 工具
- WHEN 编写需要 Mock 的测试
- THEN SHALL 使用 mockgen 自动生成 mock 实现
- THEN SHALL 在接口定义文件中使用
//go:generate注解标注生成命令 - THEN 生成的 mock SHALL 放置在
tests/mocks/目录下 - THEN SHALL 覆盖 service 和 repository 接口的 mock 生成
Requirement: 达到测试覆盖率目标
系统 SHALL 达到 > 85% 的测试覆盖率。
Scenario: 总体覆盖率
- WHEN 运行所有测试并生成覆盖率报告
- THEN 总体覆盖率 SHALL 大于 85%
- THEN 核心包(config、service、handler、conversion、repository)覆盖率 SHALL 大于 85%
Scenario: 覆盖率报告生成
- WHEN 运行测试覆盖率命令
- THEN SHALL 生成覆盖率报告文件
- THEN SHALL 支持生成 HTML 格式报告
- THEN SHALL 显示每个文件的覆盖率
Requirement: 集成到构建流程
测试 SHALL 同时集成到根目录公共流程和 backend 局部流程中。
Scenario: 运行测试命令
- WHEN 执行
make test命令 - THEN SHALL 运行 backend 核心测试、frontend 的 Vitest 单元/组件测试和 desktop 专属测试
- THEN SHALL NOT 运行 MySQL 专项测试和 frontend E2E 测试
- THEN SHALL 显示测试结果
- THEN SHALL 在测试失败时返回非零退出码
Scenario: 运行 backend 局部测试命令
- WHEN 在
backend/目录执行make test命令 - THEN SHALL 运行 backend 核心测试
- THEN SHALL NOT 运行 frontend 的 Vitest 单元/组件测试、desktop 专属测试、MySQL 专项测试或 frontend E2E 测试
Scenario: 分类测试命令
-
WHEN 在
backend/目录执行make test-unit命令 -
THEN SHALL 仅运行
./internal/...和./pkg/...下的单元测试 -
WHEN 在
backend/目录执行make test-integration命令 -
THEN SHALL 仅运行
./tests/...下的集成测试
Scenario: 覆盖率检查命令
- WHEN 在
backend/目录执行make test-coverage命令 - THEN SHALL 运行测试并生成覆盖率报告
- THEN SHALL 检查覆盖率是否达标
- THEN SHALL 在覆盖率不足时返回非零退出码
Requirement: 建立前端单元测试覆盖
前端代码 SHALL 建立单元测试覆盖,纳入整体测试覆盖率统计。
Scenario: API 层测试覆盖
- WHEN 运行前端 API 层的单元测试
- THEN SHALL 覆盖 api/client.ts 的请求封装和字段转换逻辑
- THEN SHALL 覆盖 api/providers.ts、api/models.ts、api/stats.ts 的所有函数
- THEN SHALL 使用 MSW mock API 响应
Scenario: Hooks 测试覆盖
- WHEN 运行前端 Hooks 的单元测试
- THEN SHALL 覆盖 useProviders、useModels、useStats 的查询和变更逻辑
- THEN SHALL 验证缓存失效和自动刷新行为
Requirement: 建立前端组件测试覆盖
前端 SHALL 使用 React Testing Library 建立组件测试覆盖。
Scenario: 页面组件测试覆盖
- WHEN 运行前端组件测试
- THEN SHALL 覆盖 ProviderTable 的列表渲染和交互操作
- THEN SHALL 覆盖 ProviderForm 的表单校验和提交
- THEN SHALL 覆盖 ModelForm 的表单校验和提交
- THEN SHALL 覆盖 StatsTable 的筛选和数据展示
Requirement: 建立前端 E2E 测试覆盖
前端 SHALL 使用 Playwright 建立 E2E 测试覆盖。
Scenario: 供应商管理 E2E 覆盖
- WHEN 运行 E2E 测试
- THEN SHALL 覆盖供应商创建、编辑、删除的完整用户流程
- THEN SHALL 覆盖模型创建、编辑、删除的完整用户流程
Scenario: 统计查询 E2E 覆盖
- WHEN 运行 E2E 测试
- THEN SHALL 覆盖统计页面的加载和筛选查询流程
- THEN SHALL 覆盖页面间的导航切换
Requirement: 前端测试集成到构建流程
前端测试 SHALL 集成到项目的构建和验证流程中。
Scenario: 运行前端测试命令
- WHEN 在 frontend/ 目录执行测试命令
- THEN SHALL 运行所有 Vitest 单元测试和组件测试
- THEN SHALL 显示测试结果
- THEN SHALL 在测试失败时返回非零退出码
Scenario: 运行前端 E2E 测试命令
- WHEN 在 frontend/ 目录执行 E2E 测试命令
- THEN SHALL 启动 Playwright 运行 E2E 测试
- THEN SHALL 在测试失败时返回非零退出码
Requirement: 清理 ProviderRepository 死代码
系统 SHALL 移除 ProviderRepository 中未被调用的重复方法。
Scenario: 移除死代码方法
- WHEN 审查 ProviderRepository 接口
- THEN SHALL 移除
ListEnabledModels()方法声明和实现 - THEN SHALL 移除
FindByProviderAndModelName()方法声明和实现 - THEN SHALL 确保所有现有调用者通过 ModelRepository 访问等效功能
- THEN SHALL 不影响任何运行时行为
Requirement: 使用 mockgen 生成 mock
系统 SHALL 使用 mockgen 为接口自动生成 mock 实现,替代手写 mock。
Scenario: mock 生成配置
- WHEN 在接口定义文件中添加
//go:generate mockgen注解 - THEN SHALL 为
ProviderService、ModelService、RoutingService、StatsService接口生成 mock - THEN SHALL 为
ModelRepository、ProviderRepository、StatsRepository接口生成 mock - THEN SHALL 为
ProviderClient接口生成 mock - THEN 生成的 mock SHALL 输出到
tests/mocks/目录
Scenario: 替换手写 mock
- WHEN mockgen 生成的 mock 就绪
- THEN handler 测试中的手写 mock SHALL 被替换为生成的 mock
- THEN 所有测试 SHALL 继续通过,行为不变