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
@@ -0,0 +1,88 @@
"""tests/shared/srt_entries.py 的模块级测试(数据 → 测试过程 → 验证结果)。
被测模块:`tests/shared/srt_entries.py`(按秒解析 SRT 条目,供各模块测试
计算时间轴与统计),是测试公共设施中的一个独立功能单元,拥有独立测试目录。
"""
from __future__ import annotations
from pathlib import Path
from tests.shared.srt_entries import parse_srt_entries
DATA_DIR = Path(__file__).resolve().parent.parent / "data" / "alignment"
def test_parses_single_cue_with_seconds() -> None:
"""单条字幕解析为秒级时间轴与正文。"""
# 数据:一条标准 SRT。
text = "1\n00:00:01,500 --> 00:00:02,250\n你好\n"
# 测试过程
entries = parse_srt_entries(text)
# 验证结果
assert entries == [{"start": 1.5, "end": 2.25, "text": "你好"}]
def test_keeps_empty_text_entry_with_timeline() -> None:
"""空正文条目仍计入并保留时间轴(翻译补空占位需要)。"""
# 数据:空正文 + 有正文两条。
text = "1\n00:00:01,000 --> 00:00:02,000\n\n2\n00:00:03,000 --> 00:00:04,000\n有词\n"
# 测试过程
entries = parse_srt_entries(text)
# 验证结果
assert len(entries) == 2
assert entries[0]["text"] == ""
assert entries[1]["text"] == "有词"
def test_joins_multiline_text_with_space() -> None:
"""多行正文合并为空格连接(便于关键词检索与统计)。"""
# 数据:两行正文。
text = "1\n00:00:01,000 --> 00:00:02,000\n第一行\n第二行\n"
# 测试过程与验证结果
assert parse_srt_entries(text)[0]["text"] == "第一行 第二行"
def test_stops_entry_at_next_timestamp_or_index() -> None:
"""条目在下一个时间轴或序号行处结束(不跨条吞并)。"""
# 数据:三条紧凑排列(无多余空行)。
text = (
"1\n00:00:01,000 --> 00:00:02,000\n\n"
"2\n00:00:03,000 --> 00:00:04,000\n\n"
"3\n00:00:05,000 --> 00:00:06,000\n\n"
)
# 测试过程
entries = parse_srt_entries(text)
# 验证结果
assert [e["text"] for e in entries] == ["", "", ""]
def test_parses_real_reference_subtitle_file() -> None:
"""真实参考字幕文件:条数等于时间轴行数(无丢失)。"""
# 数据:共享数据目录下的真实参考字幕。
path = DATA_DIR / "sample.reference.srt"
if not path.is_file():
import pytest
pytest.skip(f"缺少数据文件 {path}")
text = path.read_text(encoding="utf-8")
# 测试过程
entries = parse_srt_entries(text)
# 验证结果
assert len(entries) == sum(1 for line in text.splitlines() if "-->" in line)
def test_empty_input_returns_empty_list() -> None:
"""空文本返回空列表。"""
# 数据:空字符串。
# 测试过程与验证结果
assert parse_srt_entries("") == []