All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
CI / Adapter unit tests (push) Successful in 58s
CI / Adapter unit tests (pull_request) Successful in 58s
CI / validate (pull_request) Successful in 2m59s
CI / validate (push) Successful in 3m0s
CI runner installs only `pytest pytest-asyncio pyyaml`; without the molecule_runtime/a2a/claude_sdk_executor stubs, the new test_provider_resolution.py fails to collect with ModuleNotFoundError. test_adapter_prevalidate.py owned the same shims via a per-file _install_stubs(), but two files maintaining parallel stub copies eventually disagree on shape (BaseAdapter needing install_plugins_via_registry, etc.). Move the shim install + sys.path bump into tests/conftest.py so every test module shares a single canonical stub set, collected before any test imports adapter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90 lines
3.8 KiB
Python
90 lines
3.8 KiB
Python
"""Shared pytest fixtures + import shims for the adapter test suite.
|
|
|
|
`adapter.py` imports at module load:
|
|
- molecule_runtime.adapters.base (BaseAdapter, AdapterConfig, RuntimeCapabilities)
|
|
- molecule_runtime.plugins (lazy in setup(), but stubbed proactively)
|
|
- a2a.server.agent_execution (AgentExecutor)
|
|
- claude_sdk_executor (lazy in create_executor(), stubbed proactively)
|
|
|
|
In production those arrive transitively via molecule-ai-workspace-runtime.
|
|
The CI runner only installs `pytest pytest-asyncio pyyaml`, so the import
|
|
chain would fail with ModuleNotFoundError before any test collects —
|
|
exactly the failure that broke CI on the #180 fix branch (PR #4) and
|
|
caused the merge wall to block on a green local but red Gitea CI.
|
|
|
|
Putting the stub installer here (collected before any test module is
|
|
imported, per pytest semantics) means every test file can do
|
|
`from adapter import ...` at module top without a per-file boilerplate
|
|
copy. It also forces a single shape for the stubs so two files can't
|
|
silently disagree on whether `BaseAdapter` has
|
|
`install_plugins_via_registry` (see test_adapter_prevalidate's
|
|
async-setup tests, which need the method to exist on the parent class).
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import types
|
|
from dataclasses import dataclass
|
|
from unittest.mock import MagicMock
|
|
|
|
|
|
@dataclass
|
|
class _StubRuntimeCapabilities:
|
|
provides_native_session: bool = False
|
|
|
|
|
|
@dataclass
|
|
class _StubAdapterConfig:
|
|
runtime_config: object = None
|
|
config_path: str = "/tmp/configs"
|
|
system_prompt: str = ""
|
|
heartbeat: object = None
|
|
|
|
|
|
class _StubBaseAdapter:
|
|
async def install_plugins_via_registry(self, *_args, **_kwargs):
|
|
pass
|
|
|
|
|
|
def _install_stubs() -> None:
|
|
"""Install the smallest set of import shims that adapter.py needs."""
|
|
if "molecule_runtime" not in sys.modules:
|
|
mr = types.ModuleType("molecule_runtime")
|
|
mr.adapters = types.ModuleType("molecule_runtime.adapters")
|
|
mr.adapters.base = types.ModuleType("molecule_runtime.adapters.base")
|
|
mr.adapters.base.BaseAdapter = _StubBaseAdapter
|
|
mr.adapters.base.AdapterConfig = _StubAdapterConfig
|
|
mr.adapters.base.RuntimeCapabilities = _StubRuntimeCapabilities
|
|
mr.plugins = types.ModuleType("molecule_runtime.plugins")
|
|
mr.plugins.load_plugins = lambda **_kwargs: []
|
|
sys.modules["molecule_runtime"] = mr
|
|
sys.modules["molecule_runtime.adapters"] = mr.adapters
|
|
sys.modules["molecule_runtime.adapters.base"] = mr.adapters.base
|
|
sys.modules["molecule_runtime.plugins"] = mr.plugins
|
|
if "a2a" not in sys.modules:
|
|
a2a = types.ModuleType("a2a")
|
|
a2a.server = types.ModuleType("a2a.server")
|
|
a2a.server.agent_execution = types.ModuleType("a2a.server.agent_execution")
|
|
a2a.server.agent_execution.AgentExecutor = type("AgentExecutor", (), {})
|
|
sys.modules["a2a"] = a2a
|
|
sys.modules["a2a.server"] = a2a.server
|
|
sys.modules["a2a.server.agent_execution"] = a2a.server.agent_execution
|
|
if "claude_sdk_executor" not in sys.modules:
|
|
mod = types.ModuleType("claude_sdk_executor")
|
|
mod.ClaudeSDKExecutor = MagicMock(name="ClaudeSDKExecutor")
|
|
sys.modules["claude_sdk_executor"] = mod
|
|
|
|
|
|
# Run at conftest import time — pytest collects conftest.py before any
|
|
# test module, so the stubs are in sys.modules before `from adapter
|
|
# import ...` ever executes.
|
|
_install_stubs()
|
|
|
|
# adapter.py lives in the parent dir of tests/ (template root). pytest's
|
|
# `--import-mode=importlib` + tests/pytest.ini anchoring rootdir at
|
|
# tests/ means the parent isn't on sys.path automatically. Add it here
|
|
# once so every test file can do `from adapter import ...` cleanly.
|
|
_PARENT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
if _PARENT_DIR not in sys.path:
|
|
sys.path.insert(0, _PARENT_DIR)
|