Skip to content

Testing & Contracts

biopro_sdk.testing.contract

ContractTestBase

Base pytest class for plugin contract testing.

Plugin authors should subclass this in their test suite and set PLUGIN_DIR to the path of their plugin root.

Example

class TestMyPluginContract(ContractTestBase): PLUGIN_DIR = Path(file).parent.parent

Source code in src/biopro_sdk/testing/contract.py
class ContractTestBase:
    """Base pytest class for plugin contract testing.

    Plugin authors should subclass this in their test suite and set `PLUGIN_DIR`
    to the path of their plugin root.

    Example:
        class TestMyPluginContract(ContractTestBase):
            PLUGIN_DIR = Path(__file__).parent.parent
    """

    PLUGIN_DIR: Path | None = None

    @pytest.fixture
    def manifest(self) -> PluginManifest:
        if self.PLUGIN_DIR is None:
            pytest.fail("PLUGIN_DIR must be set on the test class.")

        manifest_path = self.PLUGIN_DIR / "plugin.toml"
        if not manifest_path.exists():
            pytest.fail(f"plugin.toml not found at {manifest_path}")

        with open(manifest_path, encoding="utf-8") as f:
            return PluginManifest.from_toml(f.read())

    def test_manifest_is_valid(self, manifest: PluginManifest):
        """Test that the manifest is parsable and contains required fields."""
        assert manifest.name, "Plugin name is missing or empty"
        assert manifest.entry_point, "Entry point is missing or empty"
        assert manifest.sdk_version, "SDK version is missing or empty"

    def test_headless_initialization(self, manifest: PluginManifest):
        """Test that the plugin can initialize in a headless environment without Core."""
        import importlib

        # 1. Create a mocked dictionary of all possible services the plugin could ask for
        # In a real test, you might want to use actual Mock objects for these.
        class MockService:
            pass

        mocked_services = {cap: MockService() for cap in manifest.requires}

        # 2. Build the capability-scoped context
        context = PluginContext(services=mocked_services, manifest=manifest)

        # 3. Resolve entry point
        try:
            module_name, func_name = manifest.entry_point.split(":")
        except ValueError:
            pytest.fail(f"Invalid entry_point format '{manifest.entry_point}'. Expected 'module:function'")

        try:
            module = importlib.import_module(module_name)
            init_func = getattr(module, func_name)
        except Exception as e:
            pytest.fail(f"Failed to load entry point {manifest.entry_point}: {e}")

        # 4. Attempt initialization
        try:
            plugin_instance = init_func(context)
        except Exception as e:
            pytest.fail(f"Plugin failed to initialize headlessly. Ensure it does not rely on global UI/Core state: {e}")

        assert plugin_instance is not None, "Initialization function returned None"

test_headless_initialization(manifest)

Test that the plugin can initialize in a headless environment without Core.

Source code in src/biopro_sdk/testing/contract.py
def test_headless_initialization(self, manifest: PluginManifest):
    """Test that the plugin can initialize in a headless environment without Core."""
    import importlib

    # 1. Create a mocked dictionary of all possible services the plugin could ask for
    # In a real test, you might want to use actual Mock objects for these.
    class MockService:
        pass

    mocked_services = {cap: MockService() for cap in manifest.requires}

    # 2. Build the capability-scoped context
    context = PluginContext(services=mocked_services, manifest=manifest)

    # 3. Resolve entry point
    try:
        module_name, func_name = manifest.entry_point.split(":")
    except ValueError:
        pytest.fail(f"Invalid entry_point format '{manifest.entry_point}'. Expected 'module:function'")

    try:
        module = importlib.import_module(module_name)
        init_func = getattr(module, func_name)
    except Exception as e:
        pytest.fail(f"Failed to load entry point {manifest.entry_point}: {e}")

    # 4. Attempt initialization
    try:
        plugin_instance = init_func(context)
    except Exception as e:
        pytest.fail(f"Plugin failed to initialize headlessly. Ensure it does not rely on global UI/Core state: {e}")

    assert plugin_instance is not None, "Initialization function returned None"

test_manifest_is_valid(manifest)

Test that the manifest is parsable and contains required fields.

Source code in src/biopro_sdk/testing/contract.py
def test_manifest_is_valid(self, manifest: PluginManifest):
    """Test that the manifest is parsable and contains required fields."""
    assert manifest.name, "Plugin name is missing or empty"
    assert manifest.entry_point, "Entry point is missing or empty"
    assert manifest.sdk_version, "SDK version is missing or empty"