Test a plugin¶
Test the API contract directly. You do not need a CLI, plugin manager, or source loader to exercise execute.
Test registration¶
from transpiler_mate.api import TranspilerPlugin
def test_registration() -> None:
assert isinstance(plugin, TranspilerPlugin)
assert plugin.name == "my-target"
assert plugin.options_model is Options
TranspilerPlugin is runtime-checkable, but runtime protocol checks only establish structural presence. Keep a strict type checker in the development suite to verify generic option types and call signatures.
Build a focused context¶
from pathlib import Path
from cwl_utils.parser.cwl_v1_2 import Workflow
from pydantic import AnyUrl
from transpiler_mate.api import TranspilerContext
from transpiler_mate.api.software_application_models import SoftwareApplication
class ResolverStub:
def resolve(self, location: str) -> TranspilerContext:
raise AssertionError(f"unexpected resolution of {location!r}")
def make_context(tmp_path: Path) -> TranspilerContext:
process = Workflow(id="main", inputs=[], outputs=[], steps=[])
return TranspilerContext(
source=AnyUrl((tmp_path / "workflow.cwl").as_uri()),
metadata=SoftwareApplication.model_construct(),
document={"main": process},
resolver=ResolverStub(),
)
Use model_construct() only when bypassing unrelated validation is intentional. Construct complete validated metadata when the behavior under test reads it.
Set process_id="main" in the context when testing code that reads
context.resolved_process. Leave it unset when testing plugins that operate on
the complete mapping through document or processes.
Test both failure categories¶
import pytest
from transpiler_mate.api import PluginExecutionError, PluginFailureError
def test_rejects_an_unsupported_construct(context, options) -> None:
with pytest.raises(PluginFailureError, match="scatter"):
plugin.execute(context, options)
def test_reports_generator_crash(context, options, monkeypatch) -> None:
monkeypatch.setattr(target_sdk, "generate", crash)
with pytest.raises(PluginExecutionError) as raised:
plugin.execute(context, options)
assert raised.value.__cause__ is not None
Also assert observable side effects: exact file content, encoding, directory layout, and whether partial output remains after failure. If the plugin calls context.resolver, use a recording stub and assert the location passed to it.
Run repository checks¶
For this API repository itself, the configured test command is:
hatch run test:test
Plugin projects may use any test environment, but should test against every Python and API version range they claim to support.