diff --git a/simplemem_router.py b/simplemem_router.py index 7b1188d9..5d6fe359 100644 --- a/simplemem_router.py +++ b/simplemem_router.py @@ -112,7 +112,11 @@ def register( overwrite: bool = False, ) -> None: """ - Register a memory backend. + Register a memory backend under a short *mode* name. + + After registration the backend is available via :func:`create` and + :func:`list_modes`. Built-in modes (``"text"``, ``"omni"``) are + registered automatically at import time. Args: mode: Short name used in ``create(mode=...)``. @@ -125,6 +129,30 @@ def register( init: Custom constructor ``fn(cls, **kwargs) -> instance``. If *None*, the default ``cls(**kwargs)`` is used. overwrite: Allow replacing an existing registration. + + Raises: + ValueError: If *mode* is already registered and *overwrite* is + ``False``. + + Examples:: + + # Register a custom backend + register( + mode="custom", + module_path="mypackage.memory", + class_name="MyMemorySystem", + description="My custom memory backend", + ) + mem = create(mode="custom") + + # Replace an existing registration + register( + mode="custom", + module_path="mypackage.memory_v2", + class_name="MyMemorySystemV2", + description="My improved backend", + overwrite=True, + ) """ if mode in _registry and not overwrite: raise ValueError( diff --git a/tests/test_router_register.py b/tests/test_router_register.py new file mode 100644 index 00000000..c773dc7d --- /dev/null +++ b/tests/test_router_register.py @@ -0,0 +1,113 @@ +""" +Tests for the public ``register`` function in simplemem_router. +""" +import sys +import os +import pytest + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import simplemem_router as simplemem +from simplemem_router import register, list_modes, is_available + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +_TEST_MODE = "_test_register_dummy" +_TEST_MODE_2 = "_test_register_dummy2" + + +def _cleanup(*modes): + """Remove test modes from the registry after each test.""" + # Access the private registry to clean up test entries. + import simplemem_router as _sr + for mode in modes: + _sr._registry.pop(mode, None) + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + +class TestRegister: + def test_register_adds_mode_to_list_modes(self): + """A newly registered mode appears in list_modes().""" + try: + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + description="Dummy test backend", + ) + assert _TEST_MODE in list_modes() + assert list_modes()[_TEST_MODE] == "Dummy test backend" + finally: + _cleanup(_TEST_MODE) + + def test_register_duplicate_raises_value_error(self): + """Registering an existing mode without overwrite=True raises ValueError.""" + try: + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + description="First", + ) + with pytest.raises(ValueError, match="already registered"): + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + description="Second", + ) + finally: + _cleanup(_TEST_MODE) + + def test_register_overwrite_replaces_description(self): + """overwrite=True silently replaces the existing registration.""" + try: + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + description="Original", + ) + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + description="Replaced", + overwrite=True, + ) + assert list_modes()[_TEST_MODE] == "Replaced" + finally: + _cleanup(_TEST_MODE) + + def test_register_stores_required_deps(self): + """required_deps are stored on the backend descriptor.""" + import simplemem_router as _sr + try: + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + required_deps=["os"], + ) + assert _sr._registry[_TEST_MODE].required_deps == ["os"] + finally: + _cleanup(_TEST_MODE) + + def test_register_missing_dep_not_available(self): + """is_available returns False when a required package is absent.""" + try: + register( + mode=_TEST_MODE, + module_path="os", + class_name="getcwd", + required_deps=["_nonexistent_package_xyz"], + ) + assert is_available(_TEST_MODE) is False + finally: + _cleanup(_TEST_MODE)