Files
MoviePilot/tests/test_message_notification_naming_compat.py
T
jxxghp 240a4dffe6 refactor(schemas): 统一 message/notification 命名边界,旧名收敛至兼容映射表
- notification 域:渠道能力(MessageChannel→NotificationChannel、ChannelCapability* 迁入 notification.py)
- message 域:消息收发(Notification→Message、NotificationType→MessageType、CommingMessage→IncomingMessage、NotificationHistoryItem→MessageHistoryItem、NotificationClear*→MessageClear*)
- Agent 工具契约:send_notification_message→send_message、notification_callback→message_callback
- 源码不保留旧名物理别名,旧导入经 app/runtime/compat/manifest.py SYMBOL_ALIASES 惰性解析
- API 路径与持久化键冻结不变,前端零改动
- 新增兼容守护测试与 docs/rules/07 命名边界规范
2026-08-16 19:32:20 +08:00

114 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
message/notification 命名统一的兼容守护测试
边界定义:notification 表示通知渠道能力,message 表示各渠道发送或接收的消息。
canonical 源码不保留任何旧名别名,旧名一律经由 runtime/compat 映射表
SYMBOL_ALIASES)在导入器层惰性解析,供存量插件继续使用。
"""
import importlib
from app.runtime.compat.manifest import SYMBOL_ALIASES
from app.schemas.message import (
IncomingMessage,
Message,
MessageClearBefore,
MessageClearData,
MessageClearScope,
MessageHistoryItem,
)
from app.schemas.notification import (
ChannelCapabilities,
ChannelCapability,
ChannelCapabilityManager,
)
from app.schemas.types import MessageType, NotificationChannel
# 旧名 -> canonical 对象的期望映射,覆盖全部登记的兼容入口
_EXPECTED_RESOLUTIONS = {
("app.schemas.types", "MessageChannel"): NotificationChannel,
("app.schemas.types", "NotificationType"): MessageType,
("app.schemas.message", "Notification"): Message,
("app.schemas.message", "CommingMessage"): IncomingMessage,
("app.schemas.message", "NotificationHistoryItem"): MessageHistoryItem,
("app.schemas.message", "NotificationClearScope"): MessageClearScope,
("app.schemas.message", "NotificationClearBefore"): MessageClearBefore,
("app.schemas.message", "NotificationClearData"): MessageClearData,
("app.schemas.message", "ChannelCapability"): ChannelCapability,
("app.schemas.message", "ChannelCapabilities"): ChannelCapabilities,
("app.schemas.message", "ChannelCapabilityManager"): ChannelCapabilityManager,
}
def test_canonical_modules_do_not_define_legacy_names():
"""canonical 模块自身不得保留旧名物理别名,旧名只能来自兼容映射。"""
types_module = importlib.import_module("app.schemas.types")
message_module = importlib.import_module("app.schemas.message")
for legacy_name in ("MessageChannel", "NotificationType"):
assert legacy_name not in types_module.__dict__
for legacy_name in (
"Notification",
"CommingMessage",
"NotificationHistoryItem",
"NotificationClearScope",
"NotificationClearBefore",
"NotificationClearData",
):
assert legacy_name not in message_module.__dict__
def test_manifest_registers_all_legacy_message_notification_symbols():
"""映射表必须登记全部旧名,且目标指向当前 canonical 符号。"""
for (module_name, legacy_name), canonical in _EXPECTED_RESOLUTIONS.items():
alias = SYMBOL_ALIASES[module_name][legacy_name]
target = getattr(importlib.import_module(alias.target_module), alias.target_name)
assert target is canonical, (module_name, legacy_name)
def test_legacy_symbol_imports_resolve_through_compat_hook():
"""插件旧导入路径应经兼容钩子解析到 canonical 对象。"""
for (module_name, legacy_name), canonical in _EXPECTED_RESOLUTIONS.items():
module = importlib.import_module(module_name)
assert getattr(module, legacy_name) is canonical, (module_name, legacy_name)
def test_schemas_package_level_legacy_imports_resolve():
"""from app.schemas import 旧名 的插件写法应继续可用。"""
schemas_package = importlib.import_module("app.schemas")
assert schemas_package.MessageChannel is NotificationChannel
assert schemas_package.NotificationType is MessageType
assert schemas_package.Notification is Message
assert schemas_package.CommingMessage is IncomingMessage
assert schemas_package.NotificationHistoryItem is MessageHistoryItem
assert schemas_package.ChannelCapabilityManager is ChannelCapabilityManager
def test_legacy_from_import_statement_works():
"""from ... import 语句形式的旧导入应正常执行。"""
scope: dict = {}
exec(
"from app.schemas import Notification, MessageChannel, NotificationType\n"
"from app.schemas.message import CommingMessage\n"
"from app.schemas.types import MessageChannel as LegacyChannel\n",
scope,
)
assert scope["Notification"] is Message
assert scope["MessageChannel"] is NotificationChannel
assert scope["NotificationType"] is MessageType
assert scope["CommingMessage"] is IncomingMessage
assert scope["LegacyChannel"] is NotificationChannel
def test_legacy_and_canonical_instances_interchangeable():
"""旧名构造的实例应与 canonical 类型互相兼容(同一类对象)。"""
legacy_message = importlib.import_module("app.schemas").Notification(title="t")
assert isinstance(legacy_message, Message)
assert isinstance(
Message(title="t"),
importlib.import_module("app.schemas.message").Notification,
)