blob: 9eaee310b26358e87f9ded679ca10903470f318b [file]
import sys
import types
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
from gerrit_mcp_server.extensions import ExtensionContext, ExtensionLoader
def _make_ctx(tmp_path: Path) -> ExtensionContext:
return ExtensionContext(
mcp=MagicMock(),
get_base_url=MagicMock(return_value="https://example.com"),
normalize_url=lambda u: u,
run_curl=MagicMock(),
load_config=MagicMock(return_value={}),
log_path=tmp_path / "server.log",
)
class TestExtensionLoader(unittest.TestCase):
def setUp(self):
import tempfile
self._tmp = tempfile.mkdtemp()
self.ctx = _make_ctx(Path(self._tmp))
self.loader = ExtensionLoader(self.ctx)
def test_no_extensions_returns_empty(self):
with (
patch.object(self.loader, "_discover_entry_point_modules", return_value=[]),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
):
result = self.loader.load()
self.assertEqual(result, [])
def test_successful_registration(self):
mod = types.ModuleType("fake_ext")
mod.register = MagicMock()
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["fake_ext"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch.dict(sys.modules, {"fake_ext": mod}),
):
result = self.loader.load()
self.assertEqual(result, ["fake_ext"])
mod.register.assert_called_once_with(self.ctx)
def test_import_failure_skipped(self):
register_mock = MagicMock()
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["nonexistent.module"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch(
"gerrit_mcp_server.extensions.importlib.import_module",
side_effect=ImportError("no module"),
),
):
result = self.loader.load()
self.assertEqual(result, [])
register_mock.assert_not_called()
def test_missing_register_skipped(self):
mod = types.ModuleType("no_register_ext")
# Use a NonCallableMagicMock so callable(mod.register) is False,
# matching the real "missing register" guard, while still allowing
# assert_not_called() to confirm register() was never invoked.
from unittest.mock import NonCallableMagicMock
mod.register = NonCallableMagicMock()
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["no_register_ext"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch.dict(sys.modules, {"no_register_ext": mod}),
):
result = self.loader.load()
self.assertEqual(result, [])
mod.register.assert_not_called()
def test_register_raises_skipped(self):
mod = types.ModuleType("bad_register_ext")
mod.register = MagicMock(side_effect=RuntimeError("boom"))
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["bad_register_ext"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch.dict(sys.modules, {"bad_register_ext": mod}),
):
result = self.loader.load()
self.assertEqual(result, [])
def test_deduplication_loads_once(self):
mod = types.ModuleType("dup_ext")
mod.register = MagicMock()
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["dup_ext", "dup_ext"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch.dict(sys.modules, {"dup_ext": mod}),
):
result = self.loader.load()
self.assertEqual(result, ["dup_ext"])
mod.register.assert_called_once()
def test_duplicate_logs_warning(self):
mod = types.ModuleType("dup_warn_ext")
mod.register = MagicMock()
with (
patch.object(
self.loader,
"_discover_entry_point_modules",
return_value=["dup_warn_ext", "dup_warn_ext"],
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
patch.dict(sys.modules, {"dup_warn_ext": mod}),
):
self.loader.load()
log_content = (self.ctx.log_path).read_text()
self.assertIn("WARNING", log_content)
self.assertIn("dup_warn_ext", log_content)
def test_entry_point_discovery_failure_does_not_abort(self):
# A malformed entry point in the environment can make discovery
# raise; startup must survive it (never-abort-startup contract).
with (
patch(
"gerrit_mcp_server.extensions.entry_points",
side_effect=RuntimeError("bad entry point"),
),
patch.object(self.loader, "_discover_env_modules", return_value=[]),
):
result = self.loader.load()
self.assertEqual(result, [])
log_content = (self.ctx.log_path).read_text()
self.assertIn("discovery failed", log_content)
if __name__ == "__main__":
unittest.main()