Files
MoviePilot/tests/test_service_base_contract.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

150 lines
4.1 KiB
Python
Raw Permalink 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.
"""
公共服务基类的可空配置与实例化边界。
"""
import pytest
from app.modules import ServiceBase, _ModuleBase
from app.schemas.system import NotificationConf
class _Service(ServiceBase[object, NotificationConf]):
"""
通过内存配置测试公共服务逻辑,不建立外部连接。
"""
def __init__(self, configs):
"""
保留初始化前的默认状态并记录可供加载的配置。
"""
super().__init__()
self.available_configs = configs
def get_configs(self):
"""
返回调用方指定的配置。
"""
return self.available_configs
def test_default_name_before_initialization_is_empty():
"""
初始化前不存在默认配置,应与 get_config/get_instance 一样返回 None。
"""
service = _Service({})
assert service.get_default_config_name() is None
assert service.get_config() is None
assert service.get_instance() is None
@pytest.mark.parametrize("name", [None, "named"])
def test_class_factory_preserves_name_override_and_config_identity(name):
"""
配置名称可空,构造参数中的 name 仍按原字典展开顺序覆盖配置名。
"""
token = object()
config = NotificationConf(name=name, config={"name": "override", "token": token})
service = _Service({name: config})
class Client:
"""
记录收到的原始配置参数。
"""
def __init__(self, **kwargs):
"""
保留构造参数供身份断言使用。
"""
self.kwargs = kwargs
service.init_service("client", Client)
client = service.get_instances()[name]
assert client.kwargs == {"name": "override", "token": token}
assert client.kwargs["token"] is token
assert service.get_configs()[name] is config
@pytest.mark.parametrize("name", [None, "named"])
def test_callable_factory_receives_original_configuration(name):
"""
函数工厂仍接收配置对象本身,包括名称与 config 为空的情况。
"""
config = NotificationConf(name=name, config=None)
service = _Service({name: config})
calls = []
instance = object()
def factory(conf):
"""
记录原始配置,避免被转换为构造参数。
"""
calls.append(conf)
return instance
service.init_service("client", factory)
assert calls == [config]
assert calls[0] is config
assert service.get_instances()[name] is instance
def test_class_factory_rejects_none_config_without_constructing_client():
"""
空配置继续触发原字典解包的 TypeError,不能误创建默认连接。
"""
service = _Service({"named": NotificationConf(name="named", config=None)})
calls = []
class Client:
"""
在构造被意外调用时留下可检查的证据。
"""
def __init__(self, **kwargs):
"""
记录客户端的实际创建。
"""
calls.append(kwargs)
with pytest.raises(TypeError, match="'NoneType' object is not a mapping"):
service.init_service("client", Client)
assert calls == []
assert service.get_instances() == {}
def test_missing_module_metadata_retains_implicit_none():
"""
已有模块可以只实现原抽象方法;未提供的元数据仍为 None。
"""
class Module(_ModuleBase):
"""
仅实现生命周期抽象方法的兼容模块。
"""
def init_module(self):
"""
无需初始化外部资源。
"""
def init_setting(self):
"""
没有模块设置开关。
"""
def stop(self):
"""
没有需要释放的外部资源。
"""
def test(self):
"""
没有需要连接的外部服务。
"""
module = Module()
assert module.get_name() is None
assert module.get_reload_name() is None
assert module.get_type() is None
assert module.get_subtype() is None
assert module.get_priority() is None