Add exhaustive pytest suite for Python modules#96
Open
deacon-mp wants to merge 1 commit into
Open
Conversation
Covers all 3 Python modules (magma_api.py, magma_svc.py, hook.py) with 68 tests across initialization, endpoint behavior, plugin lifecycle, route registration, and edge cases. Includes Caldera-core stubs in conftest.py so tests run standalone without the framework.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a standalone Python pytest test suite for the plugin’s Python surface area (API/service modules + hook.py lifecycle), using conftest.py stubs to avoid requiring a full Caldera runtime.
Changes:
- Adds pytest coverage for
app/magma_api.py(mirror) andapp/magma_svc.py(foo/wiring). - Adds tests for
hook.pyplugin metadata andenable()lifecycle behavior. - Introduces
tests/conftest.pyCaldera-core import stubs andpytest.iniconfiguration.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_magma_api.py |
Exercises MagmaAPI initialization and mirror() echo behavior across payload shapes and error cases. |
tests/test_magma_svc.py |
Covers MagmaService construction/wiring and async foo() behavior. |
tests/test_hook.py |
Validates hook.py module attributes and basic enable() lifecycle expectations. |
tests/conftest.py |
Provides fixtures plus Caldera-core module stubs and import wiring to run tests standalone. |
tests/__init__.py |
Marks/structures the Python test package (empty). |
pytest.ini |
Configures pytest discovery and async mode defaults. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+31
to
+100
| def _install_caldera_stubs(): | ||
| """Create minimal stand-ins for caldera packages that magma imports.""" | ||
|
|
||
| app_dir = os.path.join(PROJECT_ROOT, "app") | ||
|
|
||
| # --- top-level "app" package with __path__ pointing at real dir -------- | ||
| app_pkg = types.ModuleType("app") | ||
| app_pkg.__path__ = [app_dir] | ||
| app_pkg.__package__ = "app" | ||
|
|
||
| # --- app.utility.base_world ------------------------------------------- | ||
| app_utility = types.ModuleType("app.utility") | ||
| app_utility.__path__ = [] | ||
| app_utility.__package__ = "app.utility" | ||
|
|
||
| base_world_mod = types.ModuleType("app.utility.base_world") | ||
|
|
||
| class _Access: | ||
| APP = "app" | ||
| RED = "red" | ||
| BLUE = "blue" | ||
|
|
||
| class BaseWorld: | ||
| Access = _Access | ||
|
|
||
| base_world_mod.BaseWorld = BaseWorld | ||
|
|
||
| # --- app.service.auth_svc --------------------------------------------- | ||
| app_service = types.ModuleType("app.service") | ||
| app_service.__path__ = [] | ||
| app_service.__package__ = "app.service" | ||
|
|
||
| auth_svc_mod = types.ModuleType("app.service.auth_svc") | ||
|
|
||
| def check_authorization(func): | ||
| return func | ||
|
|
||
| def for_all_public_methods(decorator): | ||
| def wrapper(cls): | ||
| return cls | ||
| return wrapper | ||
|
|
||
| auth_svc_mod.check_authorization = check_authorization | ||
| auth_svc_mod.for_all_public_methods = for_all_public_methods | ||
|
|
||
| # --- register them in sys.modules so later imports resolve ------------ | ||
| mods = { | ||
| "app": app_pkg, | ||
| "app.utility": app_utility, | ||
| "app.utility.base_world": base_world_mod, | ||
| "app.service": app_service, | ||
| "app.service.auth_svc": auth_svc_mod, | ||
| } | ||
| for name, mod in mods.items(): | ||
| sys.modules[name] = mod | ||
|
|
||
|
|
||
| _install_caldera_stubs() | ||
|
|
||
| # We also need `plugins.magma` to resolve for hook.py's relative import. | ||
| _plugins = types.ModuleType("plugins") | ||
| _plugins.__path__ = [] | ||
| _plugins_magma = types.ModuleType("plugins.magma") | ||
| _plugins_magma.__path__ = [PROJECT_ROOT] | ||
| _plugins_magma_app = types.ModuleType("plugins.magma.app") | ||
| _plugins_magma_app.__path__ = [os.path.join(PROJECT_ROOT, "app")] | ||
|
|
||
| sys.modules["plugins"] = _plugins | ||
| sys.modules["plugins.magma"] = _plugins_magma | ||
| sys.modules["plugins.magma.app"] = _plugins_magma_app |
Comment on lines
+17
to
+27
| def _make_request(body: dict | list | str | None = None, method: str = "POST", path: str = "/"): | ||
| """Build a lightweight mock request whose .read() returns *body* as JSON bytes.""" | ||
| req = AsyncMock(spec=web.Request) | ||
| if body is not None: | ||
| payload = json.dumps(body).encode() if not isinstance(body, (bytes, bytearray)) else body | ||
| else: | ||
| payload = b"{}" | ||
| req.read = AsyncMock(return_value=payload) | ||
| req.method = method | ||
| req.path = path | ||
| return req |
Comment on lines
+64
to
+68
| @pytest.mark.asyncio | ||
| async def test_enable_retrieves_app_svc(self, mock_services, mock_app): | ||
| await hook.enable(mock_services) | ||
| mock_services["app_svc"].application # accessed as attribute | ||
|
|
| svc_a = MagmaService(mock_services) | ||
| svc_b = MagmaService(mock_services) | ||
| assert svc_a is not svc_b | ||
| assert svc_a.log is not svc_b.log or svc_a.log.name == svc_b.log.name |
| @@ -0,0 +1,3 @@ | |||
| [pytest] | |||
| testpaths = tests | |||
| asyncio_mode = auto | |||
Comment on lines
+6
to
+11
| import logging | ||
| from unittest.mock import AsyncMock, MagicMock, patch | ||
|
|
||
| import pytest | ||
| import pytest_asyncio | ||
|
|
Comment on lines
+4
to
+9
| from unittest.mock import AsyncMock, MagicMock, patch | ||
|
|
||
| import pytest | ||
| from aiohttp import web | ||
| from aiohttp.test_utils import make_mocked_request | ||
|
|
Comment on lines
+83
to
+87
| async def test_enable_accesses_application(self, mock_services, mock_app): | ||
| await hook.enable(mock_services) | ||
| # The enable function reads app_svc.application | ||
| _ = mock_services["app_svc"].application | ||
|
|
Comment on lines
+3
to
+6
| import importlib | ||
| import sys | ||
| import types | ||
| from unittest.mock import AsyncMock, MagicMock, patch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
magma_api.py,magma_svc.py, andhook.pyconftest.pywith Caldera-core stubs so tests run standalone without the full frameworkenable()), route registration, plugin contract attributes, and dist-serving address conventionspytest.inifor configurationModules covered
app/magma_api.pytests/test_magma_api.pyapp/magma_svc.pytests/test_magma_svc.pyhook.pytests/test_hook.pyTest plan
python3 -m pytest tests/ -v)