mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/jxxghp/MoviePilot.git
synced 2026-09-20 08:03:34 +08:00
138 lines
4.3 KiB
Python
138 lines
4.3 KiB
Python
"""媒体分类语义在 REST Schema 边界的兼容测试。"""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from app.schemas.category import (
|
|
ClassificationPolicy,
|
|
ClassificationResult,
|
|
ClassificationSelection,
|
|
)
|
|
from app.schemas.context import MediaInfo
|
|
from app.schemas.music import MusicAlbumInfo, MusicInfo
|
|
|
|
|
|
def _classification_result() -> ClassificationResult:
|
|
"""构造可用于序列化往返的最小分类结果。"""
|
|
selection = ClassificationSelection(
|
|
category_id="music.live",
|
|
category_path=["现场专辑"],
|
|
rule_id="rule.music.live",
|
|
source="automatic",
|
|
)
|
|
return ClassificationResult(
|
|
recommended=selection,
|
|
effective=selection,
|
|
labels=["现场"],
|
|
policy_revision=12,
|
|
state="complete",
|
|
)
|
|
|
|
|
|
def test_video_legacy_category_is_promoted_to_library_category() -> None:
|
|
"""旧影视客户端只提交 category 时应无损迁移为媒体库分类。"""
|
|
media = MediaInfo.model_validate(
|
|
{
|
|
"type": "电影",
|
|
"category": "动画/电影",
|
|
}
|
|
)
|
|
|
|
assert media.library_category == "动画/电影"
|
|
assert media.category == "动画/电影"
|
|
assert media.metadata_category == ""
|
|
|
|
|
|
def test_music_legacy_category_is_migrated_to_metadata_only() -> None:
|
|
"""旧音乐来源 category 不得被误认成媒体库目录分类。"""
|
|
music = MusicInfo.model_validate(
|
|
{
|
|
"type": "音乐",
|
|
"category": "Album / Live",
|
|
}
|
|
)
|
|
|
|
assert music.metadata_category == "Album / Live"
|
|
assert music.library_category == ""
|
|
assert music.category == ""
|
|
|
|
|
|
def test_music_explicit_new_categories_keep_compatibility_field_on_library() -> None:
|
|
"""新音乐载荷中兼容 category 始终跟随 library_category。"""
|
|
music = MusicInfo.model_validate(
|
|
{
|
|
"type": "音乐",
|
|
"category": "旧值",
|
|
"library_category": "现场专辑",
|
|
"metadata_category": "Album / Live",
|
|
}
|
|
)
|
|
|
|
assert music.library_category == "现场专辑"
|
|
assert music.metadata_category == "Album / Live"
|
|
assert music.category == "现场专辑"
|
|
|
|
|
|
def test_music_album_schema_uses_the_same_category_contract() -> None:
|
|
"""专辑 DTO 与单曲 DTO 必须采用相同的分类字段语义。"""
|
|
album = MusicAlbumInfo.model_validate(
|
|
{
|
|
"type": "音乐",
|
|
"category": "Album / Compilation",
|
|
}
|
|
)
|
|
|
|
assert album.metadata_category == "Album / Compilation"
|
|
assert album.library_category == ""
|
|
assert album.category == ""
|
|
|
|
|
|
def test_classification_snapshot_round_trip_remains_strongly_typed() -> None:
|
|
"""分类快照经过 JSON 往返后仍应恢复为 ClassificationResult。"""
|
|
music = MusicInfo(
|
|
media_source="musicbrainz",
|
|
media_id="recording-1",
|
|
title="Example",
|
|
library_category="现场专辑",
|
|
metadata_category="Album / Live",
|
|
classification=_classification_result(),
|
|
)
|
|
|
|
restored = MusicInfo.model_validate_json(music.model_dump_json())
|
|
|
|
assert isinstance(restored.classification, ClassificationResult)
|
|
assert restored.classification.policy_revision == 12
|
|
assert restored.category == "现场专辑"
|
|
assert restored.metadata_category == "Album / Live"
|
|
|
|
|
|
def test_legacy_policy_defaults_to_primary_only_enrichment_mode() -> None:
|
|
"""旧策略载荷缺少新字段时必须保持零跨源调用的兼容默认值。"""
|
|
policy = ClassificationPolicy.model_validate(
|
|
{
|
|
"schema_version": 2,
|
|
"revision": 1,
|
|
"categories": [],
|
|
"rules": [],
|
|
"fallbacks": {},
|
|
}
|
|
)
|
|
|
|
assert policy.enrichment_mode == "primary_only"
|
|
assert policy.model_dump(mode="json")["enrichment_mode"] == "primary_only"
|
|
|
|
|
|
def test_policy_rejects_unknown_enrichment_mode() -> None:
|
|
"""未知补充模式必须在持久化和执行前由严格 schema 拒绝。"""
|
|
with pytest.raises(ValidationError):
|
|
ClassificationPolicy.model_validate(
|
|
{
|
|
"schema_version": 2,
|
|
"revision": 1,
|
|
"enrichment_mode": "always_replace",
|
|
"categories": [],
|
|
"rules": [],
|
|
"fallbacks": {},
|
|
}
|
|
)
|