Merge pull request #472 from Molecule-AI/fix/remove-orphaned-plugin-tests

fix: remove orphaned plugin/adapter tests
This commit is contained in:
Hongming Wang 2026-04-16 04:39:44 -07:00 committed by GitHub
commit 3534aa0b5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 0 additions and 341 deletions

View File

@ -1,125 +0,0 @@
"""Integration tests: each first-party plugin installs via the registry pipeline.
Exercises the full flow a workspace runtime goes through at startup:
load_plugins() install_plugins_via_registry() adaptor.install(ctx)
For each combination of (plugin, runtime) declared in plugin.yaml, we verify:
- the adaptor resolves via the plugin-shipped path (not raw-drop)
- skills land in /configs/skills/<skill_name>/
- rules/fragments land in /configs/CLAUDE.md
"""
from __future__ import annotations
import logging
import sys
from pathlib import Path
import pytest
import yaml
_WS_TEMPLATE = Path(__file__).resolve().parents[1]
if str(_WS_TEMPLATE) not in sys.path:
sys.path.insert(0, str(_WS_TEMPLATE))
from plugins_registry import AdaptorSource, InstallContext, resolve # noqa: E402
_REPO_ROOT = _WS_TEMPLATE.parent
_PLUGINS_DIR = _REPO_ROOT / "plugins"
FIRST_PARTY_PLUGINS = ["molecule-dev", "superpowers", "ecc"]
@pytest.fixture
def ctx(tmp_path: Path):
configs = tmp_path / "configs"
configs.mkdir()
# Simple memory append implementation for the test.
def _append(filename: str, content: str) -> None:
target = configs / filename
existing = target.read_text() if target.exists() else ""
marker = content.splitlines()[0] if content else ""
if marker and marker in existing:
return
with open(target, "a") as f:
f.write(("\n" if existing and not existing.endswith("\n") else "") + content + "\n")
def _make(plugin_name: str, runtime: str) -> InstallContext:
return InstallContext(
configs_dir=configs,
workspace_id="ws-test",
runtime=runtime,
plugin_root=_PLUGINS_DIR / plugin_name,
append_to_memory=_append,
logger=logging.getLogger(plugin_name),
)
return _make, configs
@pytest.mark.parametrize("plugin_name", FIRST_PARTY_PLUGINS)
def test_plugin_manifest_declares_runtimes(plugin_name: str):
"""Each first-party plugin must declare supported runtimes."""
manifest = yaml.safe_load((_PLUGINS_DIR / plugin_name / "plugin.yaml").read_text())
assert "runtimes" in manifest, f"{plugin_name} missing `runtimes:` in plugin.yaml"
assert "claude_code" in manifest["runtimes"]
assert "deepagents" in manifest["runtimes"]
@pytest.mark.parametrize("plugin_name", FIRST_PARTY_PLUGINS)
@pytest.mark.parametrize("runtime", ["claude_code", "deepagents"])
def test_plugin_ships_adaptor_file(plugin_name: str, runtime: str):
"""Each declared runtime has a physical adaptor file."""
adaptor_file = _PLUGINS_DIR / plugin_name / "adapters" / f"{runtime}.py"
assert adaptor_file.is_file(), f"{plugin_name} missing adapters/{runtime}.py"
@pytest.mark.parametrize("plugin_name", FIRST_PARTY_PLUGINS)
@pytest.mark.parametrize("runtime", ["claude_code", "deepagents"])
def test_adaptor_resolves_via_plugin_path(plugin_name: str, runtime: str):
"""resolve() must find the plugin-shipped adaptor, not fall back to raw-drop."""
plugin_root = _PLUGINS_DIR / plugin_name
_, source = resolve(plugin_name, runtime, plugin_root)
assert source == AdaptorSource.PLUGIN
@pytest.mark.parametrize("plugin_name,runtime", [
(p, r) for p in FIRST_PARTY_PLUGINS for r in ["claude_code", "deepagents"]
])
async def test_plugin_installs_end_to_end(plugin_name: str, runtime: str, ctx):
"""Installing each plugin writes the expected content into /configs."""
make_ctx, configs_dir = ctx
plugin_root = _PLUGINS_DIR / plugin_name
adaptor, source = resolve(plugin_name, runtime, plugin_root)
assert source == AdaptorSource.PLUGIN
result = await adaptor.install(make_ctx(plugin_name, runtime))
assert result.plugin_name == plugin_name
# If the plugin has skills/, each one should now exist under /configs/skills/
src_skills = plugin_root / "skills"
if src_skills.is_dir():
for skill in src_skills.iterdir():
if skill.is_dir():
assert (configs_dir / "skills" / skill.name).is_dir(), \
f"{plugin_name}: skill {skill.name} not copied"
# If the plugin has rules/, CLAUDE.md should contain the marker.
src_rules = plugin_root / "rules"
if src_rules.is_dir() and any(p.suffix == ".md" for p in src_rules.iterdir()):
claude_md = configs_dir / "CLAUDE.md"
assert claude_md.exists(), f"{plugin_name}: CLAUDE.md not created"
assert f"# Plugin: {plugin_name} /" in claude_md.read_text()
async def test_install_is_idempotent(ctx):
"""Installing molecule-dev twice leaves a single marker, doesn't duplicate."""
make_ctx, configs_dir = ctx
plugin_root = _PLUGINS_DIR / "molecule-dev"
adaptor, _ = resolve("molecule-dev", "claude_code", plugin_root)
await adaptor.install(make_ctx("molecule-dev", "claude_code"))
await adaptor.install(make_ctx("molecule-dev", "claude_code"))
# At least one skill dir exists; CLAUDE.md has exactly one marker section header.
assert (configs_dir / "skills" / "review-loop").is_dir()

View File

@ -1,115 +0,0 @@
"""Smoke tests for the Hermes adapter.
Verifies:
1. Required files exist under adapters/hermes/
2. requirements.txt declares openai>=1.0.0 (primary runtime dep)
3. discover_adapters() completes without error
4. Other adapters (e.g. langgraph) are unaffected
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
HERMES_DIR = Path(__file__).parent.parent / "adapters" / "hermes"
# ---------------------------------------------------------------------------
# Fixture: isolate adapter cache and sys.modules per test
# ---------------------------------------------------------------------------
@pytest.fixture(autouse=True)
def _reset_adapter_cache():
"""Clear the module-level adapter cache and evict hermes from sys.modules
before each test so loader state is fresh, then restore afterwards."""
import adapters as pkg
original_cache = dict(pkg._ADAPTER_CACHE)
pkg._ADAPTER_CACHE.clear()
evicted = {k: sys.modules.pop(k) for k in list(sys.modules)
if k == "adapters.hermes" or k.startswith("adapters.hermes.")}
yield
# Restore
pkg._ADAPTER_CACHE.clear()
pkg._ADAPTER_CACHE.update(original_cache)
sys.modules.update(evicted)
# ---------------------------------------------------------------------------
# 1. Directory layout — PR-1 shell must have exactly these three files
# ---------------------------------------------------------------------------
class TestHermesShellLayout:
def test_directory_exists(self):
assert HERMES_DIR.is_dir(), "adapters/hermes/ directory is missing"
@pytest.mark.skip(
reason="Dockerfile moved to molecule-ai-workspace-template-hermes standalone repo"
)
def test_dockerfile_present(self):
assert (HERMES_DIR / "Dockerfile").is_file(), "Dockerfile missing from hermes shell"
def test_init_py_present(self):
assert (HERMES_DIR / "__init__.py").is_file(), "__init__.py missing from hermes shell"
@pytest.mark.skip(
reason="requirements.txt moved to molecule-ai-workspace-template-hermes standalone repo"
)
def test_requirements_txt_present(self):
assert (HERMES_DIR / "requirements.txt").is_file(), "requirements.txt missing"
# ---------------------------------------------------------------------------
# 2. requirements.txt — primary dependency contract
# NOTE: requirements.txt has moved to the standalone template repo.
# The source-of-truth checks below are now in that repo's own test suite.
# ---------------------------------------------------------------------------
class TestHermesRequirements:
@pytest.mark.skip(
reason="requirements.txt moved to molecule-ai-workspace-template-hermes standalone repo"
)
def test_openai_version_pin(self):
text = (HERMES_DIR / "requirements.txt").read_text()
assert "openai>=1.0.0" in text, (
"Expected 'openai>=1.0.0' in requirements.txt — "
"the Hermes adapter relies on the OpenAI-compat client for Nous Portal / OpenRouter."
)
@pytest.mark.skip(
reason="requirements.txt moved to molecule-ai-workspace-template-hermes standalone repo"
)
def test_no_heavy_framework_deps(self):
"""PR-1 shell must not introduce heavy deps that aren't committed to yet."""
text = (HERMES_DIR / "requirements.txt").read_text().lower()
heavy = ["langchain", "crewai", "autogen", "langgraph"]
found = [dep for dep in heavy if dep in text]
assert not found, f"Unexpected heavy deps in hermes requirements.txt: {found}"
# ---------------------------------------------------------------------------
# 3. Loader integration
# ---------------------------------------------------------------------------
class TestHermesLoaderIntegration:
def test_discover_does_not_raise(self):
"""discover_adapters() must complete without raising."""
from adapters import discover_adapters
result = discover_adapters()
assert isinstance(result, dict)
def test_other_adapters_unaffected(self):
"""Hermes registration must not block other adapters from loading.
langgraph has no heavy optional deps and should always be discoverable."""
from adapters import discover_adapters
result = discover_adapters()
assert "langgraph" in result, (
"langgraph adapter missing — loader may have short-circuited."
)

View File

@ -1,101 +0,0 @@
"""Drift guard: the SDK's AgentskillsAdaptor must stay behaviourally in
sync with the runtime's copy.
The SDK vendors its own copy so plugin authors can unit-test without
depending on workspace-template, but a behavioural divergence would be
silent a user fixes a rules-injection bug in one copy and the other
goes on emitting the wrong output. This test runs the same install
scenario through both copies and asserts the observable side effects
are identical (CLAUDE.md contents + skill files on disk + InstallResult
payload).
"""
from __future__ import annotations
import logging
import sys
from pathlib import Path
def _add_to_path(p: Path) -> None:
if str(p) not in sys.path:
sys.path.insert(0, str(p))
_REPO = Path(__file__).resolve().parents[2]
_add_to_path(_REPO / "workspace-template")
_add_to_path(_REPO / "sdk" / "python")
from plugins_registry.builtins import AgentskillsAdaptor as RuntimeAdaptor # noqa: E402
from plugins_registry.protocol import InstallContext as RuntimeCtx # noqa: E402
from molecule_plugin.builtins import AgentskillsAdaptor as SDKAdaptor # noqa: E402
from molecule_plugin.protocol import InstallContext as SDKCtx # noqa: E402
def _make_plugin(root: Path) -> Path:
(root / "rules").mkdir(parents=True)
(root / "rules" / "r1.md").write_text("- rule one")
(root / "fragment.md").write_text("frag text")
(root / "README.md").write_text("skip me")
(root / "skills" / "s1").mkdir(parents=True)
(root / "skills" / "s1" / "SKILL.md").write_text(
"---\nname: s1\ndescription: d\n---\nbody"
)
# setup.sh proves both adaptors run the hook (drift guard for the
# plugin-owned dependency-install step). The marker file lets the
# test assert the script actually executed.
setup = root / "setup.sh"
setup.write_text(
'#!/bin/bash\nset -e\ntouch "$CONFIGS_DIR/setup-ran-marker"\n'
)
setup.chmod(0o755)
return root
def _memory_sink(store: dict):
def _append(filename: str, content: str) -> None:
store.setdefault(filename, "")
store[filename] = (store[filename] + ("\n" if store[filename] else "") + content + "\n")
return _append
async def _install(adaptor_cls, ctx_cls, plugin: Path, configs: Path) -> tuple[list[str], dict]:
mem: dict = {}
ctx = ctx_cls(
configs_dir=configs,
workspace_id="ws",
runtime="claude_code",
plugin_root=plugin,
append_to_memory=_memory_sink(mem),
logger=logging.getLogger("drift"),
)
result = await adaptor_cls("my-plugin", "claude_code").install(ctx)
return sorted(result.files_written), mem
async def test_sdk_and_runtime_produce_identical_side_effects(tmp_path: Path):
"""SDK.install() and runtime.install() must yield byte-identical
memory text and skill-file placement for the same input plugin."""
plugin_runtime = _make_plugin(tmp_path / "plugin-a")
plugin_sdk = _make_plugin(tmp_path / "plugin-b")
configs_runtime = tmp_path / "configs-a"
configs_runtime.mkdir()
configs_sdk = tmp_path / "configs-b"
configs_sdk.mkdir()
rt_files, rt_mem = await _install(RuntimeAdaptor, RuntimeCtx, plugin_runtime, configs_runtime)
sdk_files, sdk_mem = await _install(SDKAdaptor, SDKCtx, plugin_sdk, configs_sdk)
assert rt_files == sdk_files, "copied-files lists diverge"
assert rt_mem == sdk_mem, (
"CLAUDE.md contents diverge between SDK and runtime AgentskillsAdaptor"
)
# Both adaptors must run setup.sh — the marker file proves it. If only
# one side runs the hook, plugin authors get false-pass unit tests
# against the SDK while production behaves differently.
assert (configs_runtime / "setup-ran-marker").is_file(), (
"runtime AgentskillsAdaptor did not execute setup.sh"
)
assert (configs_sdk / "setup-ran-marker").is_file(), (
"SDK AgentskillsAdaptor did not execute setup.sh — drift from runtime"
)