mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-14 18:24:42 +08:00
236 lines
8.1 KiB
Python
236 lines
8.1 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding:utf-8 -*-
|
||
"""
|
||
通知模板渲染回归测试。
|
||
|
||
V3 起通知模板不再硬编码在程序中,默认模板由数据库升级迁移写入
|
||
``SystemConfigKey.NotificationTemplates``。本测试验证:
|
||
|
||
1. V3 迁移会把默认模板一次性写入数据库配置,并覆盖用户旧设置;
|
||
2. 带双引号条件的 Jinja 模板(如 ``{% if type == "音乐" %}``)可正常渲染,
|
||
避免 JSON 序列化转义引号导致音乐下载/入库通知内容为空的历史问题;
|
||
3. 模板完全由数据库配置驱动,配置缺失时不渲染、消息保持原样。
|
||
"""
|
||
from pathlib import Path
|
||
|
||
import importlib.util
|
||
|
||
import pytest
|
||
|
||
from app.core.context import MUSIC_ENTITY_ALBUM, MusicInfo
|
||
from app.core.meta import MetaMusic
|
||
from app.db.systemconfig_oper import SystemConfigOper
|
||
from app.helper.message import MessageTemplateHelper, TemplateContextBuilder, TemplateHelper
|
||
from app.schemas.message import Notification
|
||
from app.schemas.types import ContentType, SystemConfigKey
|
||
|
||
MUSIC_ORGANIZE_TEMPLATE = """
|
||
{
|
||
'title': '{{ title_year }}{% if track_number %} #{{ track_number }}{% endif %}'
|
||
'{% if season_episode %} {{ season_episode }}{% endif %} 已入库',
|
||
'text': '类型:{{ type }}{% if category %},类别:{{ category }}{% endif %}'
|
||
'{% if type == "音乐" and artist %}\\n艺术家:{{ artist }}{% endif %}'
|
||
'{% if type == "音乐" and album %}\\n专辑:{{ album }}{% endif %}'
|
||
'{% if type == "音乐" and audio_specs %}\\n音质:{{ audio_specs }}{% endif %}'
|
||
'{% if resource_term %},质量:{{ resource_term }}{% endif %}'
|
||
',共{{ file_count }}个文件,大小:{{ total_size }}'
|
||
}"""
|
||
|
||
MUSIC_CONTEXT = {
|
||
"type": "音乐",
|
||
"title_year": "晴天 (2003)",
|
||
"track_number": 3,
|
||
"artist": "周杰伦",
|
||
"album": "叶惠美",
|
||
"audio_specs": "FLAC · 24-bit · 96 kHz",
|
||
"resource_term": "无损",
|
||
"file_count": 12,
|
||
"total_size": "1.2 GB",
|
||
}
|
||
|
||
|
||
@pytest.fixture()
|
||
def notification_templates() -> SystemConfigOper:
|
||
"""备份并还原通知模板配置,避免测试污染其它用例。"""
|
||
config_oper = SystemConfigOper()
|
||
original = config_oper.get(SystemConfigKey.NotificationTemplates)
|
||
yield config_oper
|
||
config_oper.set(SystemConfigKey.NotificationTemplates, original)
|
||
|
||
|
||
def test_album_batch_context_uses_album_title_for_notification() -> None:
|
||
"""
|
||
专辑实体批量入库的通知上下文应以专辑名作为标题,
|
||
不得展示批次中某个单曲的曲名和曲序。
|
||
"""
|
||
meta = MetaMusic(
|
||
title="晴天",
|
||
artists=["周杰伦"],
|
||
album="叶惠美",
|
||
track_number=3,
|
||
year=2003,
|
||
)
|
||
mediainfo = MusicInfo(
|
||
music_type=MUSIC_ENTITY_ALBUM,
|
||
title="晴天",
|
||
album="叶惠美",
|
||
artists=["周杰伦"],
|
||
year=2003,
|
||
)
|
||
|
||
context = TemplateContextBuilder().build(
|
||
meta=meta, mediainfo=mediainfo, aggregate_music_album=True
|
||
)
|
||
|
||
assert context["title"] == "叶惠美"
|
||
assert context["title_year"] == "叶惠美 (2003)"
|
||
assert context.get("track_number") is None
|
||
|
||
rendered = TemplateHelper().render(
|
||
template_content=MUSIC_ORGANIZE_TEMPLATE,
|
||
**dict(context, file_count=11, total_size="1.2 GB"),
|
||
)
|
||
assert isinstance(rendered, dict)
|
||
assert rendered["title"] == "叶惠美 (2003) 已入库"
|
||
assert "艺术家:周杰伦" in rendered["text"]
|
||
assert "#3" not in rendered["title"]
|
||
|
||
|
||
def test_album_batch_context_keeps_track_identity_for_rename() -> None:
|
||
"""
|
||
重命名等逐文件场景(未开启整专聚合)应继续使用每个文件的曲名和曲序,
|
||
不能把专辑名写成所有目标文件名。
|
||
"""
|
||
meta = MetaMusic(
|
||
title="晴天",
|
||
artists=["周杰伦"],
|
||
album="叶惠美",
|
||
track_number=3,
|
||
year=2003,
|
||
)
|
||
mediainfo = MusicInfo(
|
||
music_type=MUSIC_ENTITY_ALBUM,
|
||
title="晴天",
|
||
album="叶惠美",
|
||
artists=["周杰伦"],
|
||
year=2003,
|
||
)
|
||
|
||
context = TemplateContextBuilder().build(meta=meta, mediainfo=mediainfo)
|
||
|
||
assert context["title"] == "晴天"
|
||
assert context["track"] == "03"
|
||
|
||
|
||
def test_single_track_context_keeps_track_title_and_number() -> None:
|
||
"""
|
||
单曲下载/整理的通知上下文应继续使用曲目标题和曲序,
|
||
不受专辑场景调整的影响。
|
||
"""
|
||
context = MUSIC_CONTEXT.copy()
|
||
|
||
rendered = TemplateHelper().render(
|
||
template_content=MUSIC_ORGANIZE_TEMPLATE, **context
|
||
)
|
||
|
||
assert isinstance(rendered, dict)
|
||
assert rendered["title"] == "晴天 (2003) #3 已入库"
|
||
|
||
|
||
def test_literal_template_with_quoted_condition_renders_music() -> None:
|
||
"""
|
||
带 ``{% if type == "音乐" %}`` 双引号条件的模板应能正常渲染,
|
||
音乐字段(艺术家/专辑/音质)必须出现在渲染结果中。
|
||
"""
|
||
rendered = TemplateHelper().render(
|
||
template_content=MUSIC_ORGANIZE_TEMPLATE, **MUSIC_CONTEXT
|
||
)
|
||
|
||
assert isinstance(rendered, dict)
|
||
assert rendered["title"] == "晴天 (2003) #3 已入库"
|
||
assert "艺术家:周杰伦" in rendered["text"]
|
||
assert "专辑:叶惠美" in rendered["text"]
|
||
assert "音质:FLAC · 24-bit · 96 kHz" in rendered["text"]
|
||
|
||
|
||
def test_literal_template_skips_music_fields_for_video() -> None:
|
||
"""
|
||
影视场景下模板中的音乐专属字段不应出现,影视字段正常渲染。
|
||
"""
|
||
rendered = TemplateHelper().render(
|
||
template_content=MUSIC_ORGANIZE_TEMPLATE,
|
||
type="电视剧",
|
||
title_year="测试剧集 (2025)",
|
||
season_episode="S01E05",
|
||
category="国产剧",
|
||
resource_term="1080p",
|
||
file_count=1,
|
||
total_size="5 GB",
|
||
)
|
||
|
||
assert isinstance(rendered, dict)
|
||
assert rendered["title"] == "测试剧集 (2025) S01E05 已入库"
|
||
assert "艺术家" not in rendered["text"]
|
||
assert "专辑" not in rendered["text"]
|
||
assert "类别:国产剧" in rendered["text"]
|
||
|
||
|
||
def test_message_renders_from_db_config(notification_templates: SystemConfigOper) -> None:
|
||
"""
|
||
MessageTemplateHelper 应使用数据库配置的模板渲染消息,
|
||
而不再依赖程序内硬编码模板。
|
||
"""
|
||
notification_templates.set(
|
||
SystemConfigKey.NotificationTemplates,
|
||
{"organizeSuccess": MUSIC_ORGANIZE_TEMPLATE},
|
||
)
|
||
message = Notification(ctype=ContentType.OrganizeSuccess)
|
||
|
||
MessageTemplateHelper.render(message, **MUSIC_CONTEXT)
|
||
|
||
assert message.title == "晴天 (2003) #3 已入库"
|
||
assert "艺术家:周杰伦" in message.text
|
||
|
||
|
||
def test_message_without_template_config_stays_unchanged(
|
||
notification_templates: SystemConfigOper,
|
||
) -> None:
|
||
"""
|
||
数据库中没有模板配置时消息应保持原样,不应渲染也不应报错。
|
||
"""
|
||
notification_templates.set(SystemConfigKey.NotificationTemplates, None)
|
||
message = Notification(ctype=ContentType.OrganizeSuccess)
|
||
|
||
MessageTemplateHelper.render(message, **MUSIC_CONTEXT)
|
||
|
||
assert message.title is None
|
||
assert message.text is None
|
||
|
||
|
||
def test_v3_migration_overwrites_templates_once(
|
||
notification_templates: SystemConfigOper,
|
||
) -> None:
|
||
"""
|
||
V3 大版本迁移应无条件覆盖用户旧通知模板配置,并写入全部 4 类模板。
|
||
"""
|
||
migration_path = (
|
||
Path(__file__).resolve().parent.parent
|
||
/ "database" / "versions" / "4dadad1d161a_3_0_0.py"
|
||
)
|
||
spec = importlib.util.spec_from_file_location("v3_migration", migration_path)
|
||
migration = importlib.util.module_from_spec(spec)
|
||
spec.loader.exec_module(migration)
|
||
|
||
notification_templates.set(
|
||
SystemConfigKey.NotificationTemplates, {"organizeSuccess": "用户自定义模板"}
|
||
)
|
||
migration.upgrade()
|
||
|
||
templates = notification_templates.get(SystemConfigKey.NotificationTemplates)
|
||
assert set(templates.keys()) == {
|
||
"organizeSuccess", "downloadAdded", "subscribeAdded", "subscribeComplete",
|
||
}
|
||
assert templates["organizeSuccess"] != "用户自定义模板"
|
||
assert "音乐" in templates["organizeSuccess"]
|
||
assert "音乐" in templates["downloadAdded"]
|