import pytest from markitect_tool.extension import ( ExtensionDescriptor, ExtensionRegistry, ExtensionRegistryError, OptionalDependency, ProcessingCapability, ) def test_extension_descriptor_serializes_contract_metadata(): descriptor = ExtensionDescriptor( id="query.selector", kind="query-engine", summary="Small selector query engine.", capabilities=[ProcessingCapability(id="ast", kind="read")], input_contract="Document + selector", output_contract="QueryMatch[]", diagnostics_namespace="query", provenance_prefix="query.selector", cli={"command": "mkt query"}, docs=["docs/query-extraction.md"], ) data = descriptor.to_dict() assert data["id"] == "query.selector" assert data["kind"] == "query-engine" assert data["capabilities"][0]["id"] == "ast" assert data["cli"]["command"] == "mkt query" def test_extension_registry_lists_by_kind_and_capability(): selector = ExtensionDescriptor( id="query.selector", kind="query-engine", capabilities=[ProcessingCapability(id="ast")], ) local = ExtensionDescriptor( id="backend.local-sqlite", kind="backend", capabilities=[ProcessingCapability(id="snapshots"), ProcessingCapability(id="fts")], ) registry = ExtensionRegistry([local, selector]) assert [descriptor.id for descriptor in registry.list()] == [ "backend.local-sqlite", "query.selector", ] assert [descriptor.id for descriptor in registry.list(kind="query-engine")] == [ "query.selector" ] assert [descriptor.id for descriptor in registry.require_capability("fts")] == [ "backend.local-sqlite" ] assert registry.require_capability("missing") == [] assert registry.list(kind="missing") == [] def test_extension_registry_rejects_duplicate_ids(): descriptor = ExtensionDescriptor(id="query.selector", kind="query-engine") registry = ExtensionRegistry([descriptor]) with pytest.raises(ExtensionRegistryError, match="Duplicate extension id"): registry.register(descriptor) def test_extension_registry_checks_optional_dependencies(): registry = ExtensionRegistry( [ ExtensionDescriptor( id="query.jsonpath", kind="query-engine", optional_dependencies=[ OptionalDependency( name="jsonpath_ng", package="jsonpath-ng", extra="query", required=True, ), OptionalDependency(name="tabulate"), ], ) ] ) missing = registry.check_dependencies("query.jsonpath", available_modules=set()) available = registry.check_dependencies( "query.jsonpath", available_modules={"jsonpath_ng", "tabulate"}, ) assert not missing.compatible assert missing.missing == ["jsonpath_ng"] assert missing.optional_missing == ["tabulate"] assert available.compatible def test_extension_descriptor_instantiates_factory(): descriptor = ExtensionDescriptor( id="fake.extension", kind="test", factory=lambda: {"ready": True}, ) assert descriptor.instantiate() == {"ready": True} def test_extension_descriptor_requires_factory_to_instantiate(): descriptor = ExtensionDescriptor(id="fake.extension", kind="test") with pytest.raises(ExtensionRegistryError, match="has no factory"): descriptor.instantiate()