From 0ceb09df2aecd89d7f22cf7621514b6c3f3bba50 Mon Sep 17 00:00:00 2001 From: Misaka Date: Sat, 4 Apr 2026 18:45:54 +0800 Subject: [PATCH] docs: add P2 test fix plan (13 failures to 0) - Detailed analysis of 3 failing test files - Task breakdown: logger.test.ts (11 failures), update-service (1), update-installer (1) - Estimated effort: 3-4 hours - Solution blueprints for each failure type --- docs/P2_TEST_FIX_PLAN.md | 493 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 docs/P2_TEST_FIX_PLAN.md diff --git a/docs/P2_TEST_FIX_PLAN.md b/docs/P2_TEST_FIX_PLAN.md new file mode 100644 index 0000000..8b85c56 --- /dev/null +++ b/docs/P2_TEST_FIX_PLAN.md @@ -0,0 +1,493 @@ +# P2 测试修复执行计划 + +**创建日期**: 2026-04-04 +**优先级**: P2 - 中等优先级 +**预计工时**: 3-4 小时 +**目标**: 将测试通过率从 95% 提升至 100% + +--- + +## 📊 当前状态分析 + +### 失败测试分布 + +| 测试文件 | 失败数量 | 根因分类 | 预计工时 | +|---------|---------|---------|---------| +| `logger.test.ts` | 11 failures | Winston format mock + 循环依赖 | 2-3h | +| `update-service.test.ts` | 1 failure | Mock 参数不匹配 | 30min | +| `update-installer.test.ts` | 1 failure | 路径断言错误 | 15min | +| **总计** | **13 failures** | - | **~3-4h** | + +### 测试通过率 + +| 指标 | 当前 | 修复后 | +|------|------|--------| +| 失败套件 | 3 suites | 0 suites | +| 失败测试 | 13 tests | 0 tests | +| 通过率 | 95% (311/327) | 100% (327/327) | + +--- + +## 🎯 任务分解 + +--- + +### Task 2.1: 修复 logger.test.ts (11 失败) + +**优先级**: P2-High +**预计工时**: 2-3 小时 +**依赖**: 无 +**阻塞**: 11 个测试失败 + +#### 问题诊断 + +**失败模式**: +``` +TypeError: __vite_ssr_import_0__.default.format(...) is not a function + at src/main/services/logger/index.ts:114:4 +``` + +**根因分析**: + +1. **直接原因**: `logger.test.ts` 中的 winston format mock 与全局 `tests/setup.ts` 的 mock 冲突或覆盖不完整 +2. **深层原因**: `logger.ts` 和 `config-manager.ts` 存在双向依赖,导致初始化顺序问题 +3. **具体表现**: 第 114 行的 `winston.format()` 链式调用在 mock 环境中返回 undefined + +**调用栈**: +``` +logger.test.ts + → imports logger.ts + → calls winston.format().combine().timestamp().printf() + → format mock returns undefined + → TypeError +``` + +**文件位置**: +- 测试文件:`tests/unit/logger.test.ts` +- 被 mock 文件:`src/main/services/logger/index.ts:100-116` +- Setup mock: `tests/setup.ts` (无 winston mock 冲突) + +#### 解决方案 + +**方案 A: 完善 logger.test.ts 的 winston mock (推荐,1 小时)** + +**步骤 2.1.1**: 检查当前 mock 实现 + +```typescript +// 读取 tests/unit/logger.test.ts 第 18-68 行 +// 确认 wi nston mock 格式 +``` + +**步骤 2.1.2**: 创建完整的可链式 format mock + +```typescript +// tests/unit/logger.test.ts - 替换现有的 format mock + +function createFormatFn() { + // format 函数本身 - 当以 format() 形式调用时 + const formatFn = vi.fn((callback?: Function) => { + if (callback) { + return { transform: callback } + } + return formatFn + }) as any + + // 链式方法 - 全部返回 formatFn 自身以支持链式调用 + formatFn.combine = vi.fn((...formats: any[]) => formatFn) + formatFn.timestamp = vi.fn((options?: any) => formatFn) + formatFn.colorize = vi.fn(() => formatFn) + formatFn.printf = vi.fn((callback: Function) => { + return { transform: callback } + }) + formatFn.json = vi.fn(() => formatFn) + formatFn.simple = vi.fn(() => formatFn) + formatFn.pretty = vi.fn(() => formatFn) + formatFn.label = vi.fn((options?: any) => formatFn) + formatFn.errors = vi.fn(() => formatFn) + formatFn.metadata = vi.fn(() => formatFn) + formatFn.cli = vi.fn(() => formatFn) + + return formatFn +} + +const format = createFormatFn() + +vi.mock('winston', () => ({ + default: { + format, + createLogger: vi.fn(() => createLoggerInstance), + transports: { + Console: vi.fn(), + DailyRotateFile: vi.fn(), + File: vi.fn() + } + } +})) +``` + +**步骤 2.1.3**: 添加额外的 error mock + +```typescript +// logger.test.ts 中,确保 format().errors() 也被支持 +// 因为在 logger/index.ts 中可能调用 format.errors({ stack: true }) +``` + +**方案 B: 将 logger.test.ts 转为集成测试 (2 小时)** + +如果 mock 过于复杂,可以考虑: +- 使用 vi.resetModules() 确保每次测试都重新加载 +- 使用 vi.mock(importOriginal) 混合真实模块 +- 或完全重写测试,只测试 logger 的公共 API + +**预期结果**: +- ✅ 18/18 tests passing +- ✅ format().combine().timestamp().printf() 链式调用正常工作 +- ✅ logger 创建、子 logger、日志输出测试全部通过 + +#### 成功标准 + +- [ ] `npm run test:run tests/unit/logger.test.ts` → 18/18 through +- [ ] 无 `format(...) is not a function` 类型错误 +- [ ] 所有 logger 方法测试断言通过 +- [ ] ConfigManager 集成测试通过 + +--- + +### Task 2.2: 修复 update-service.test.ts (1 失败) + +**优先级**: P2-Medium +**预计工时**: 30 分钟 +**依赖**: 无 +**阻塞**: 1 个测试失败 + +#### 问题诊断 + +**失败测试**: `checks updates for user and auto-downloads available recommendation` + +**错误信息**: +``` +AssertionError: expected "vi.fn()" to be called with arguments: + ['stable/1.1.0.exe', 'preview/1.1.0.exe'] + +Number of calls: 0 +``` + +**根因**: Mock 调用参数与实际调用不匹配 + +**代码位置**: +- 测试文件:`tests/unit/update-service.test.ts:165-175` +- 被测文件:`src/main/services/update/update-service.ts` + +#### 解决方案 + +**步骤 2.2.1**: 读取测试代码 + +```typescript +// 读取 tests/unit/update-service.test.ts:165-180 +it('checks updates for user and auto-downloads available recommendation', async () => { + // 模拟场景... + expect(mockDownload).toHaveBeenCalledWith('stable/1.1.0.exe', 'preview/1.1.0.exe') +}) +``` + +**步骤 2.2.2**: 检查实际调用 + +```typescript +// 查看实际调用参数是什么 +// 可能是 mockDownload.mock.calls +``` + +**步骤 2.2.3**: 更新测试断言 + +**选项 A: 匹配实际调用** +```typescript +// 如果实际只调用了一个参数 +expect(mockDownload).toHaveBeenCalledWith('stable/1.1.0.exe') +``` + +**选项 B: 使用更松散的断言** +```typescript +// 如果参数顺序或数量有变化 +expect(mockDownload).toHaveBeenCalled() +expect(mockDownload.mock.calls[0]).toContain('stable/1.1.0.exe') +``` + +**选项 C: 调整 mock 设置** +```typescript +// 确保 mock 正确设置 +mockDownload.mockClear() +// ... 触发动作 ... +expect(mockDownload).toHaveBeenCalledWith( + expect.stringContaining('stable'), + expect.any(String) +) +``` + +#### 成功标准 + +- [ ] `npm run test:run tests/unit/update-service.test.ts` → 4/4 through +- [ ] 断言与实际调用匹配 +- [ ] 测试描述的行为得到验证 + +--- + +### Task 2.3: 修复 update-installer.test.ts (1 失败) + +**优先级**: P2-Medium +**预计工时**: 15 分钟 +**依赖**: 无 +**阻塞**: 1 个测试失败 + +#### 问题诊断 + +**失败测试**: `builds downloaded package path under userData pending-update` + +**错误信息**: +``` +AssertionError: expected 'D:\...\test-user-data\pending-update\stable-1.2.3.exe' + to contain 'logs\pending-update' + +Expected: "logs\pending-update" +Received: "D:\...\test-user-data\pending-update\stable-1.2.3.exe" +``` + +**根因**: Electron mock 的 `app.getPath('userData')` 返回 `test-user-data`,但测试期望路径包含 `logs` + +**代码位置**: +- 测试文件:`tests/unit/update-installer.test.ts:13-16` +- Setup mock: `tests/setup.ts:17-24` + +#### 解决方案 + +**步骤 2.3.1**: 修改测试断言以匹配实际 mock + +```typescript +// tests/unit/update-installer.test.ts + +// 从: +expect(result).toContain('logs\\pending-update') + +// 改为: +expect(result).toContain('test-user-data\\pending-update') +``` + +**或**: + +**步骤 2.3.2**: 修改 Electron mock 的 userData 路径 + +```typescript +// tests/setup.ts + +// 从: +userData: path.join(process.cwd(), 'test-user-data') + +// 改为: +userData: path.join(process.cwd(), 'logs') +``` + +**推荐**: 方案 2.3.1 (测试适应 mock) +- 理由:mock 是为了测试隔离,测试应该适应 mock 环境 + +#### 成功标准 + +- [ ] `npm run test:run tests/unit/update-installer.test.ts` → 2/2 through +- [ ] 路径断言与 Electron mock 一致 +- [ ] 测试仍然验证正确的业务逻辑 + +--- + +## ✅ 验证步骤 + +### 阶段验证 1: Logger 测试修复 + +```bash +# 运行 logger 测试 +npm run test:run tests/unit/logger.test.ts + +# 期望输出: +# Test Files 1 passed (1) +# Tests 18 passed (18) +``` + +**失败时排查**: +1. 检查 vi.mock 是否在文件顶部 (hoisted) +2. 清除 vitest 缓存:`npx vitest --clearCache` +3. 检查是否有多个 winston mock 冲突 + +--- + +### 阶段验证 2: Update 测试修复 + +```bash +# 运行 update 测试 +npm run test:run tests/unit/update-service.test.ts tests/unit/update-installer.test.ts + +# 期望输出: +# Test Files 2 passed (2) +# Tests 6 passed (6) +``` + +--- + +### 最终验证: 全量测试 + +```bash +# 运行完整测试套件 +npm run test:run + +# 期望输出: +# Test Files 41 passed (41) +# Tests 327 passed (327) +# Duration ~6s +``` + +```bash +# 验证 100% 通过率 +npm run test:run 2>&1 | Select-String "Test Files.*failed" + +# 期望输出: 无匹配 (0 failed) +``` + +--- + +## 📞 成功标准 + +### 技术指标 + +| 指标 | 修复前 | 修复后 | 验证命令 | +|------|-------|-------|---------| +| 失败套件 | 3 suites | 0 suites | `npm run test:run` | +| 失败测试 | 13 tests | 0 tests | `npm run test:run` | +| 通过率 | 95% (311/327) | **100%** (327/327) | 测试报告 | + +### 验收条件 + +- [ ] **零失败**: 所有 327 个测试 100% 通过 +- [ ] **零回归**: 现有 311 个测试仍然通过 +- [ ] **代码质量**: 修改的代码不引入新的 LSP 错误 +- [ ] **可维护性**: mock 和断言清晰可读 + +--- + +## ⚠️ 风险评估 + +### 技术风险 + +| 风险 | 可能性 | 影响 | 缓解措施 | +|------|--------|------|---------| +| logger mock 实现复杂 | 中 | 高 | 采用延迟 mock,先跑通一部分测试 | +| 链式调用 mock 不完整 | 高 | 中 | 使用 createFormatFn 工厂函数 | +| 循环依赖难解耦 | 低 | 高 | 只修复 mock,不重构依赖关系 | + +### 时间风险 + +- **乐观估计**: 2 小时 (一切顺利) +- **可能情况**: 3-4 小时 (mock 调试) +- **保守估计**: 6 小时 (遇到意外问题) + +**风险缓解**: 如果 logger mock 问题超过 3 小时无法解决,考虑: +1. 暂时跳过 logger.test.ts (保持 95% 通过率) +2. 先修复简单的 update 测试 (13 failures → 2 failures) +3. 记录问题,后续专门花精力解决 + +--- + +## 📝 执行记录模板 + +### Task 2.1: Logger Tests + +**开始时间**: HH:MM +**结束时间**: HH:MM +**实际工时**: X 小时 + +**修复步骤**: +1. [ ] 诊断 mock 问题 +2. [ ] 实现 formatFn 工厂 +3. [ ] 添加所有链式方法 +4. [ ] 处理 format.errors() 特殊情况 +5. [ ] 验证测试通过 + +**遇到的问题**: +- 问题 1: [描述] → 解决方案: [方案] +- 问题 2: [描述] → 解决方案: [方案] + +**关键代码**: +```typescript +// 最终有效的 mock 实现 +``` + +--- + +### Task 2.2: Update Service Test + +**开始时间**: HH:MM +**结束时间**: HH:MM +**实际工时**: X 分钟 + +**修复方式**: +- [ ] 修改断言 +- [ ] 修改 mock 参数 +- [ ] 其他: [描述] + +**结果**: ✅ Passed + +--- + +### Task 2.3: Update Installer Test + +**开始时间**: HH:MM +**结束时间**: HH:MM +**实际工时**: X 分钟 + +**修复方式**: +- [ ] 修改断言 +- [ ] 修改 mock +- [ ] 其他: [描述] + +**结果**: ✅ Passed + +--- + +## 🎯 后续改进建议 + +### 短期 (P2 修复完成后) + +1. **Mock 模式文档化** + - 创建 tests/mocks/README.md + - 记录 winston, electron, TypeORM mock 模式 + - 提供模板代码供未来测试复用 + +2. **测试分类完善** + - 考虑将 logger.test.ts 转为 integration test + - 添加 @integration 标签 + - 分离 unit 和 integration 测试 + +### 中期 (技术债务减少) + +3. **logger.ts 解耦** + - 提取 LoggerConfigProvider 接口 + - 避免与 config-manager 的循环依赖 + - 支持可插拔配置源 + +4. **Mock 中心化管理** + - 创建 tests/mocks/winston.ts + - 创建 tests/mocks/electron.ts + - 减少重复 mock 代码 + +### 长期 (测试文化建立) + +5. **CI 门禁** + - PR 必须通过全部 unit tests + - 不允许引入新的 skip 测试 + - 测试失败自动 block merge + +6. **测试驱动开发** + - 新功能必须先写测试 + - 代码审查包含测试检查 + - 测试覆盖率和代码覆盖率同等重要 + +--- + +**计划制定者**: Sisyphus AI Agent +**执行优先级**: P2 +**状态**: 待执行