1 Commits
Author SHA1 Message Date
kira1928andClaude Sonnet 4.6 13c32ff68d test: 添加 FFmpeg 异步初始化 e2e 测试,并修复 code review 发现的两个 Bug
## e2e 测试基础设施

### 测试辅助工具
- test/fake-ffmpeg/main.go: 极简 FFmpeg 替身程序,响应 -version 参数
- test/ffmpeg-mock-server/main.go: 模拟 FFmpeg 下载服务器
  - 启动时自动编译 fake-ffmpeg 并打包成 zip(含 bin/ffmpeg.exe 路径)
  - GET /ffmpeg.zip: 支持 ?speed=<字节/秒> 限速和 ?fail=true 失败模拟
  - POST /control: 运行时动态修改限速/失败行为
  - GET /health: 等待 zip 就绪后返回 200(供 Playwright 做启动检查)

### 测试 Fixture
- tests/e2e/fixtures/test-tools-config.json: remotetools 测试配置,将所有
  平台的 FFmpeg 下载 URL 重定向至 127.0.0.1:8890/ffmpeg.zip
- tests/e2e/fixtures/ffmpeg-test-config.template.yml: bgo 测试配置
  - 端口 8085(避免与正常测试冲突)
  - ffmpeg_path 指向不存在路径(强制走 remotetools 下载流程)
  - 独立 app_data_path(避免与正常测试的缓存互相干扰)

### 后端调试注入端点(dev 构建专有)
- src/servers/debug_routes_dev.go: POST /api/debug/ffmpeg/force-state
  供 e2e 测试注入任意 FFmpeg 状态,无需实际触发下载
- src/servers/debug_routes_stub.go: 非 dev 构建的空实现
- src/servers/server.go: 注册 registerDevDebugRoutes(apiRoute)
- src/tools/tools.go: 导出 ForceFFmpegStatus() 供调试端点调用
- src/tools/dev_config.go: 支持 REMOTETOOLS_CONFIG 环境变量覆盖
  remotetools 配置路径,使下载流程测试可指向 mock server

### E2E 测试文件
- tests/e2e/ffmpeg-status.spec.ts: 9 条状态注入测试(在正常配置下运行)
  - 验证 ready/checking/downloading/not_found/error 各状态的 Banner 显示
  - 验证状态切换后 Banner 正确消失
  - 验证 GET /api/ffmpeg/status 返回格式和 Alert 类型
- tests/e2e/ffmpeg-download.spec.ts: 3 条完整下载流程测试(需 mock server)
  - 慢速下载时出现 downloading Banner,加速后 Banner 消失
  - 下载失败时显示 error Banner
  - 页面刷新后正确恢复当前状态
- playwright.ffmpeg.config.ts: 下载流程测试专用配置
  - 运行 ffmpeg-mock-server 和使用独立 bgo 实例(端口 8085)
  - 通过 REMOTETOOLS_CONFIG 环境变量注入测试用 remotetools 配置
  - 每次运行前清空 external_tools 目录,确保测试从干净状态开始
- playwright.config.ts: 新增 testIgnore 排除 ffmpeg-download.spec.ts
  (该文件需要 mock server,仅在 playwright.ffmpeg.config.ts 下运行)

## Bug 修复(对应 PR #1150 code review)

### Fix 1: WaitForToolsInit 错误分支死代码(tools.go)
原代码将 WaitForToolsInit 返回的所有非 nil 错误都当作 ctx 取消处理,
但 WaitForToolsInit 在 Init() 失败时也返回该错误,导致:
- FFmpeg 状态永久停留在 "checking"
- not_found/error 状态永远不会被设置
修复:通过 ctx.Err() != nil 区分 ctx 取消和 Init 失败,失败时
正确调用 setFFmpegStatus({State: "not_found"})

### Fix 2: 前端竞态条件(ffmpeg-banner/index.tsx)
原代码将 handleSSEMessage 定义在 useEffect 外部(useCallback),
导致:HTTP 初始化请求如果晚于 SSE 推送到达,会用旧状态覆盖新状态,
使 Banner 重新出现并永久卡在旧状态;且组件卸载后 HTTP 回调仍会
调用 setStatus 造成内存泄漏。
修复:
- 将 handler 移入 useEffect,添加 active flag 防止卸载后更新
- 添加 sseReceived flag,HTTP 响应到达时若已收到 SSE 则忽略
- 移除不再需要的 useCallback import

### Fix 3: FFmpegAsyncInit 过早访问 remotetools(tools.go)
原注释称"先检查系统 PATH / 配置文件中的 FFmpeg,不需要 remotetools",
但实际调用 utils.IsFFmpegExist → utils.GetFFmpegPath → remotetools.Get().GetTool(),
在 Init() 完成前访问未配置好的 remotetools,可能产生误判。
修复:新增 isFFmpegAvailableWithoutRemotetools() 仅检查配置路径
(os.Stat)和系统 PATH(exec.LookPath),不触碰 remotetools。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 01:02:30 +09:00