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:
@@ -0,0 +1,88 @@
|
||||
"""src/wov_app/logging.py 的模块级测试(数据 → 测试过程 → 验证结果)。
|
||||
|
||||
被测模块:`src/wov_app/logging.py`(控制台日志配置),可独立调用。
|
||||
用例使用真实 logging 模块与真实日志记录,验证命名、处理器去重与传播设置。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from wov_app.logging import _APP_LOGGER_NAME, _ensure_console_handler, get_logger
|
||||
|
||||
|
||||
def _strip_handlers(logger: logging.Logger) -> None:
|
||||
"""清空日志器处理器,保证用例从干净状态开始(模块自建隔离)。"""
|
||||
for handler in list(logger.handlers):
|
||||
logger.removeHandler(handler)
|
||||
|
||||
|
||||
def test_get_logger_uses_app_prefix() -> None:
|
||||
"""日志器名称带应用前缀,便于与其他库日志区分。"""
|
||||
# 数据:模块名 "scheduler"。
|
||||
# 测试过程
|
||||
logger = get_logger("scheduler")
|
||||
|
||||
# 验证结果
|
||||
assert logger.name == f"{_APP_LOGGER_NAME}.scheduler"
|
||||
_strip_handlers(logger)
|
||||
|
||||
|
||||
def test_get_logger_attaches_console_handler_once() -> None:
|
||||
"""重复获取同一日志器不会重复附加处理器(避免日志重复打印)。"""
|
||||
# 数据:先清空,再连续获取两次。
|
||||
logger = get_logger("dup")
|
||||
_strip_handlers(logger)
|
||||
|
||||
# 测试过程
|
||||
first = get_logger("dup")
|
||||
second = get_logger("dup")
|
||||
|
||||
# 验证结果:处理器只有 1 个。
|
||||
assert first is second
|
||||
assert len([h for h in second.handlers if isinstance(h, logging.StreamHandler)]) == 1
|
||||
_strip_handlers(second)
|
||||
|
||||
|
||||
def test_get_logger_sets_info_level_and_no_propagation() -> None:
|
||||
"""日志级别为 INFO 且不向根日志传播(防止 uvicorn 重复输出)。"""
|
||||
# 数据:新日志器。
|
||||
logger = get_logger("levels")
|
||||
_strip_handlers(logger)
|
||||
|
||||
# 测试过程
|
||||
logger = get_logger("levels")
|
||||
|
||||
# 验证结果
|
||||
assert logger.level == logging.INFO
|
||||
assert logger.propagate is False
|
||||
_strip_handlers(logger)
|
||||
|
||||
|
||||
def test_ensure_console_handler_is_idempotent() -> None:
|
||||
"""_ensure_console_handler 对已配置的日志器不再追加处理器。"""
|
||||
# 数据:手工配置过的日志器。
|
||||
logger = logging.getLogger("vrsub.idempotent")
|
||||
_strip_handlers(logger)
|
||||
|
||||
# 测试过程
|
||||
_ensure_console_handler(logger)
|
||||
_ensure_console_handler(logger)
|
||||
|
||||
# 验证结果
|
||||
assert len(logger.handlers) == 1
|
||||
_strip_handlers(logger)
|
||||
|
||||
|
||||
def test_log_record_is_actually_emitted(capsys) -> None:
|
||||
"""日志记录真实写出到控制台(handler 生效,而非仅配置)。"""
|
||||
# 数据:日志器 + 一条 INFO 记录。
|
||||
logger = get_logger("emit")
|
||||
|
||||
# 测试过程
|
||||
logger.info("测试日志 %s", "生效")
|
||||
|
||||
# 验证结果:输出内容包含格式化后的消息。
|
||||
captured = capsys.readouterr()
|
||||
assert "测试日志 生效" in (captured.err + captured.out)
|
||||
_strip_handlers(logger)
|
||||
Reference in New Issue
Block a user