mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
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 命名边界规范
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
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,
|
||||
)
|
||||
Reference in New Issue
Block a user