Files
MoviePilot/tests/test_application_shared_utilities.py
2026-09-16 07:17:07 +08:00

105 lines
4.1 KiB
Python

"""Application 重复逻辑收敛后的共享纯函数回归测试。"""
from pathlib import Path
from types import SimpleNamespace
import pytest
from app.application.download.validation import validate_torrent_hash
from app.application.server.payload import build_subscribe_payload
from app.domain.classification.conditions import condition_field_ids
from app.schemas.category import ClassificationCondition, ClassificationConditionGroup
PROJECT_ROOT = Path(__file__).parents[1]
def test_application_refactor_modules_follow_single_word_leaf_names() -> None:
"""本轮新增应用模块使用单词文件名,并按同一模块进入子目录。"""
application_root = PROJECT_ROOT / "app" / "application"
refactored_files = [
application_root / "download" / "validation.py",
application_root / "history" / "__init__.py",
application_root / "history" / "contracts.py",
application_root / "history" / "mutation.py",
application_root / "history" / "retry.py",
application_root / "messaging" / "channel" / "__init__.py",
application_root / "messaging" / "channel" / "admin.py",
application_root / "messaging" / "interaction" / "__init__.py",
application_root / "messaging" / "interaction" / "agent.py",
application_root / "messaging" / "webagent" / "__init__.py",
application_root / "messaging" / "webagent" / "events.py",
application_root / "messaging" / "webagent" / "stream.py",
application_root / "server" / "payload.py",
application_root / "transfer" / "jobs.py",
application_root / "transfer" / "models.py",
application_root / "transfer" / "notifications.py",
application_root / "transfer" / "projection.py",
]
assert all(path.is_file() for path in refactored_files)
assert all(path.stem == "__init__" or "_" not in path.stem for path in refactored_files)
obsolete_files = [
application_root / "history.py",
application_root / "history_contracts.py",
application_root / "history_retry.py",
application_root / "historymutation.py",
application_root / "messaging" / "agent_interaction.py",
application_root / "messaging" / "channel_admin.py",
application_root / "messaging" / "web_agent_events.py",
application_root / "messaging" / "webagentstream.py",
]
assert all(not path.exists() for path in obsolete_files)
def test_condition_field_ids_preserves_nested_declaration_order() -> None:
"""条件树公共遍历必须保留 all/any/not 的声明顺序。"""
node = ClassificationConditionGroup(
all=[
ClassificationCondition(field="media.title", operator="exists"),
ClassificationConditionGroup(
any=[
ClassificationCondition(field="media.year", operator="exists"),
ClassificationConditionGroup(
not_=ClassificationCondition(
field="media.adult",
operator="exists",
)
),
]
),
]
)
assert condition_field_ids(node) == (
"media.title",
"media.year",
"media.adult",
)
@pytest.mark.parametrize("value", ["", "a" * 39, "g" * 40, "a" * 41])
def test_validate_torrent_hash_rejects_non_v1_hash(value: str) -> None:
"""下载应用服务必须拒绝长度或字符集不符合 v1 Hash 的输入。"""
with pytest.raises(ValueError, match="hash 格式无效"):
validate_torrent_hash(value)
def test_build_subscribe_payload_filters_fields_and_requires_identity() -> None:
"""中心服务公共载荷投影只保留白名单字段并要求媒体身份完整。"""
payload = build_subscribe_payload(
{
"name": "Demo",
"media_source": "tmdb",
"media_id": "123",
"private": "hidden",
},
{"name", "media_source", "media_id"},
)
assert payload == {
"name": "Demo",
"media_source": "themoviedb",
"media_id": "123",
}
assert build_subscribe_payload(SimpleNamespace(), {"name"}) is None