ReMe/tests/unit/test_component_registry.py
jinliyl ff2956a41f
refactor: simplify core, Studio, and unit test scaffolding (#658)
* refactor: share backend search, file responses, and vector digests

* refactor(studio): remove unused scaffolding and share workspace updates

* test: simplify async tests and share deterministic store fixtures
2026-10-22 19:35:21 +08:00

164 lines
4.8 KiB
Python

"""Tests for ComponentRegistry."""
# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access,unused-argument
import pytest
from reme.components.base_component import BaseComponent
from reme.components.component_registry import ComponentRegistry, R, create_application_registry
from reme.enumeration import ComponentEnum
class _DummyComponent(BaseComponent):
component_type = ComponentEnum.FILE_CHUNKER
class _AnotherComponent(BaseComponent):
component_type = ComponentEnum.KEYWORD_INDEX
class _NoComponentType:
pass
class _BaseComponentType(BaseComponent):
component_type = ComponentEnum.BASE
# -- register & get -----------------------------------------------------------
def test_register_direct_with_explicit_name():
reg = ComponentRegistry()
reg.register(_DummyComponent, "my_parser")
assert reg.get(ComponentEnum.FILE_CHUNKER, "my_parser") is _DummyComponent
def test_register_direct_defaults_to_class_name():
reg = ComponentRegistry()
reg.register(_DummyComponent)
assert reg.get(ComponentEnum.FILE_CHUNKER, "_DummyComponent") is _DummyComponent
def test_register_decorator():
reg = ComponentRegistry()
@reg.register("alias")
class MyParser(BaseComponent):
component_type = ComponentEnum.FILE_CHUNKER
assert reg.get(ComponentEnum.FILE_CHUNKER, "alias") is MyParser
def test_register_rejects_missing_component_type():
reg = ComponentRegistry()
with pytest.raises(TypeError, match="component_type"):
reg.register(_NoComponentType, "bad")
def test_register_plugin_defined_component_type():
reg = ComponentRegistry()
class PluginComponent(BaseComponent):
component_type = "example.reranker"
reg.register(PluginComponent, "cross_encoder")
assert reg.get("example.reranker", "cross_encoder") is PluginComponent
assert reg.get_all("example.reranker") == {"cross_encoder": PluginComponent}
def test_register_rejects_unsafe_plugin_component_type():
reg = ComponentRegistry()
class UnsafePluginComponent(BaseComponent):
component_type = "../outside"
with pytest.raises(TypeError, match="component_type"):
reg.register(UnsafePluginComponent, "unsafe")
def test_register_rejects_empty_name():
reg = ComponentRegistry()
with pytest.raises(ValueError, match="empty"):
reg._do_register(_DummyComponent, "")
def test_register_rejects_non_class_non_string():
reg = ComponentRegistry()
with pytest.raises(TypeError, match="Expected a class or string"):
reg.register(42)
# -- get_all ------------------------------------------------------------------
def test_get_all_returns_copy():
reg = ComponentRegistry()
reg.register(_DummyComponent, "a")
reg.register(_AnotherComponent, "b")
parsers = reg.get_all(ComponentEnum.FILE_CHUNKER)
assert parsers == {"a": _DummyComponent}
indexes = reg.get_all(ComponentEnum.KEYWORD_INDEX)
assert indexes == {"b": _AnotherComponent}
# Mutating the copy doesn't affect the registry.
parsers["hacked"] = _DummyComponent
assert "hacked" not in reg.get_all(ComponentEnum.FILE_CHUNKER)
def test_get_all_unknown_type_returns_empty():
reg = ComponentRegistry()
assert not reg.get_all(ComponentEnum.AS_LLM)
# -- get (miss) ---------------------------------------------------------------
def test_get_nonexistent_returns_none():
reg = ComponentRegistry()
assert reg.get(ComponentEnum.FILE_CHUNKER, "nope") is None
# -- unregister ---------------------------------------------------------------
def test_unregister_existing():
reg = ComponentRegistry()
reg.register(_DummyComponent, "x")
assert reg.unregister(ComponentEnum.FILE_CHUNKER, "x") is True
assert reg.get(ComponentEnum.FILE_CHUNKER, "x") is None
def test_unregister_missing_returns_false():
reg = ComponentRegistry()
assert reg.unregister(ComponentEnum.FILE_CHUNKER, "nope") is False
# -- clear --------------------------------------------------------------------
def test_clear():
reg = ComponentRegistry()
reg.register(_DummyComponent, "a")
reg.register(_AnotherComponent, "b")
reg.clear()
assert not reg.get_all(ComponentEnum.FILE_CHUNKER)
assert not reg.get_all(ComponentEnum.KEYWORD_INDEX)
def test_builtin_registry_is_frozen_after_package_bootstrap():
assert R.frozen is True
with pytest.raises(RuntimeError, match="frozen"):
R.register(_DummyComponent, "runtime-mutation")
def test_application_registry_copy_remains_mutable():
reg = create_application_registry()
assert reg.frozen is False
reg.register(_DummyComponent, "runtime-component")
assert reg.get(ComponentEnum.FILE_CHUNKER, "runtime-component") is _DummyComponent
assert R.get(ComponentEnum.FILE_CHUNKER, "runtime-component") is None