mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-29 03:18:14 +08:00
150 lines
4.1 KiB
Python
150 lines
4.1 KiB
Python
"""
|
||
公共服务基类的可空配置与实例化边界。
|
||
"""
|
||
|
||
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
|