test: 按模块重写测试代码,删除旧平铺结构

按"测试规则"重写 tests/:一个模块一个目录、用例按数据→过程→验证三段书写、
不保留全局 conftest.py、测试过程只调用真实生产代码。

结构(73 个文件、30 个模块目录、477 用例):
- tests/nodes/  15 个模块目录(srt/whisper/ass/ffmpeg/frame_extract/vlm/
  subtitle_ocr/llm/llm_filter/subtitle_cleanup/subtitle_correction/
  proper_nouns/adaptive_pool/vad_profiler/echo);
- tests/app/    11 个模块目录(db/scheduler/batch/maintenance/registry/seed/
  storage/config/logging/main/routers 三组 API);
- tests/sdk/test_models、tests/web/test_crop、tests/shared(公共设施)。

测试数据随模块目录入库(tests/**/data/),删除根级 testdata/;.gitignore
的 data/ 改为 /data/,否则会连带忽略 tests/**/data/ 导致测试数据无法入库。

顺带发现并修复三个真实缺陷:
- nodes/srt.py:相邻条目缺少空行时把下一条时间轴吞进正文(静默错位),
  改为正文行遇时间戳行即报错;
- src/wov_app/scheduler.py:_file_size 只捕获 OSError,含 \x00 的产物 URI
  抛 ValueError 导致任务误判失败,改为同时捕获;
- nodes/subtitle_correction.py:生产代码依赖测试包解析 SRT,
  改用生产模块 nodes/srt.py。

真实模型/服务集成测试按外部状态跳过:新增 tests/shared/gpu_memory.py
(运行时探测显存、CUDA OOM 转跳过)与 tests/shared/llm_service.py
(无 Key / 余额 / 限流转跳过)。全量 477 passed。
This commit is contained in:
2026-09-13 15:40:56 +08:00
parent 966f3e6b4b
commit 8a715a8064
139 changed files with 20810 additions and 10733 deletions
+127
View File
@@ -0,0 +1,127 @@
"""src/wov_app/seed.py 的模块级测试(数据 → 测试过程 → 验证结果)。
被测模块:`src/wov_app/seed.py`(从 workflows/*.json 载入内置工作流,幂等),
可独立调用。用例使用真实仓库工作流 JSON 与临时 SQLite 库。
"""
from __future__ import annotations
import json
from pathlib import Path
from wov_app.db import Database
from wov_app.seed import seed_default_workflows
# 仓库根与真实内置工作流目录。
WORKSPACE = Path(__file__).resolve().parents[3]
WORKFLOWS_DIR = WORKSPACE / "workflows"
def _db(tmp_path: Path) -> Database:
"""每个用例一个临时 SQLite 库。"""
return Database(tmp_path / "wov.db")
def test_seed_loads_all_builtin_workflows(tmp_path: Path) -> None:
"""真实 workflows/*.json 全部写入库,且数量与文件数一致。"""
# 数据:仓库真实工作流数据文件。
db = _db(tmp_path)
expected = {p.stem for p in WORKFLOWS_DIR.glob("*.json")}
assert expected, "仓库应至少有一个内置工作流数据文件"
# 测试过程
created = seed_default_workflows(db, WORKFLOWS_DIR)
stored = {w["id"] for w in db.list_workflows()}
# 验证结果
assert created == len(expected)
assert stored == expected
def test_seed_creates_version_record_with_valid_dag(tmp_path: Path) -> None:
"""每个工作流写入 v1 版本记录,DAG 通过结构校验(节点/边合法)。"""
# 数据:真实工作流目录。
db = _db(tmp_path)
# 测试过程
seed_default_workflows(db, WORKFLOWS_DIR)
# 验证结果:以真实 OCR 工作流为例,节点与版本齐全。
version = db.get_latest_workflow_version("ocr-subtitle")
assert version is not None
definition = version["definition"]
node_ids = {n["id"] for n in definition["nodes"]}
assert {"extract", "ocr", "filter"} <= node_ids
assert definition["edges"]
def test_seed_is_idempotent(tmp_path: Path) -> None:
"""重复 seed 不覆盖已有工作流,第二次返回创建数 0。"""
# 数据:先 seed 一次。
db = _db(tmp_path)
first = seed_default_workflows(db, WORKFLOWS_DIR)
# 测试过程:再次 seed。
second = seed_default_workflows(db, WORKFLOWS_DIR)
# 验证结果
assert first > 0
assert second == 0
assert len(db.list_workflows()) == first
def test_seed_does_not_overwrite_user_modification(tmp_path: Path) -> None:
"""已存在的工作流不被 seed 覆盖(保护用户改过的数据)。"""
# 数据:先写入一个与内置同 ID 的自定义工作流。
db = _db(tmp_path)
db.upsert_workflow({
"id": "zh-direct", "name": "用户改过的名字", "description": "", "published": 0,
"latest_version": 1,
})
# 测试过程
seed_default_workflows(db, WORKFLOWS_DIR)
# 验证结果:用户版本保留。
assert db.get_workflow("zh-direct")["name"] == "用户改过的名字"
def test_seed_from_empty_directory_creates_nothing(tmp_path: Path) -> None:
"""空目录不创建任何工作流(数据驱动,无硬编码兜底)。"""
# 数据:空目录。
empty = tmp_path / "workflows"
empty.mkdir()
db = _db(tmp_path)
# 测试过程
created = seed_default_workflows(db, empty)
# 验证结果
assert created == 0
assert db.list_workflows() == []
def test_seed_marks_workflows_published(tmp_path: Path) -> None:
"""内置工作流默认已发布(用户端可直接选择执行)。"""
# 数据:真实工作流目录。
db = _db(tmp_path)
# 测试过程
seed_default_workflows(db, WORKFLOWS_DIR)
# 验证结果
for workflow in db.list_workflows():
assert workflow["published"] == 1
def test_seed_honors_version_from_data_file(tmp_path: Path) -> None:
"""版本号取自数据文件(不同工作流可有不同当前版本)。"""
# 数据:真实工作流目录(含 v7 的 ocr-subtitle)。
db = _db(tmp_path)
# 测试过程
seed_default_workflows(db, WORKFLOWS_DIR)
# 验证结果:与数据文件声明一致。
payload = json.loads((WORKFLOWS_DIR / "ocr-subtitle.json").read_text(encoding="utf-8"))
assert db.get_workflow("ocr-subtitle")["latest_version"] == int(payload.get("version", 1))