09be821a38
CI / build (push) Successful in 16s
CI / security (push) Has been cancelled
CI / unit_tests (push) Has been cancelled
CI / benchmark-publish (push) Has been cancelled
CI / e2e_tests (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / status-check (push) Has been cancelled
CI / helm (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / quality (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / integration_tests (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
Implements issue #1520. The version command now displays the actual git commit SHA to help identify which version of the code is running. Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me> Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
797 lines
23 KiB
Python
797 lines
23 KiB
Python
"""Step definitions for plugin_extension_points.feature.
|
|
|
|
Tests all 30 spec-defined extension points: registration, lookup
|
|
by name, lookup by category, protocol type correctness, entry point
|
|
discovery, context subsystem wiring, and idempotent registration.
|
|
|
|
Based on issue #939.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.infrastructure.plugins.extension_catalog import (
|
|
TOTAL_EXTENSION_POINTS,
|
|
get_extension_point_definitions,
|
|
get_extension_points_by_category,
|
|
register_all_extension_points,
|
|
)
|
|
from cleveragents.infrastructure.plugins.extension_protocols import (
|
|
ALL_EXTENSION_PROTOCOLS,
|
|
A2AExtensionMethodExtension,
|
|
A2ATransportExtension,
|
|
ConfigSourceExtension,
|
|
ConfigValidatorExtension,
|
|
ContextPipelineComponentExtension,
|
|
ContextStorageBackendExtension,
|
|
ContextStrategyExtension,
|
|
EventFilterExtension,
|
|
EventHandlerExtension,
|
|
OutputFormatExtension,
|
|
OutputMaterializerExtension,
|
|
OutputRendererExtension,
|
|
ResourceResolverExtension,
|
|
ResourceTypeHandlerExtension,
|
|
SafetyGuardrailExtension,
|
|
SkillProviderExtension,
|
|
SkillTemplateExtension,
|
|
ToolMiddlewareExtension,
|
|
ToolProviderExtension,
|
|
ValidationRuleProviderExtension,
|
|
ValidationRunnerExtension,
|
|
)
|
|
from cleveragents.infrastructure.plugins.manager import PluginManager
|
|
from cleveragents.infrastructure.plugins.types import ExtensionPoint
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fresh PluginManager
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a fresh PluginManager for extension point registration")
|
|
def step_fresh_pm(context: Context) -> None:
|
|
context.ep_manager = PluginManager()
|
|
|
|
|
|
@given("a PluginManager with all extension points registered")
|
|
def step_pm_with_all_eps(context: Context) -> None:
|
|
context.ep_manager = PluginManager()
|
|
register_all_extension_points(context.ep_manager)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Register all extension points
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I register all spec-defined extension points")
|
|
def step_register_all(context: Context) -> None:
|
|
context.ep_registered_count = register_all_extension_points(
|
|
context.ep_manager,
|
|
)
|
|
|
|
|
|
@when("I register all spec-defined extension points again")
|
|
def step_register_all_again(context: Context) -> None:
|
|
register_all_extension_points(context.ep_manager)
|
|
|
|
|
|
@then("exactly {count:d} extension points should be registered")
|
|
def step_exactly_n_registered(context: Context, count: int) -> None:
|
|
eps = context.ep_manager.list_extension_points()
|
|
assert len(eps) == count, f"Expected {count}, got {len(eps)}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Extension point definitions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I retrieve the extension point definitions")
|
|
def step_retrieve_defs(context: Context) -> None:
|
|
context.ep_definitions = get_extension_point_definitions()
|
|
|
|
|
|
@then("I should get exactly {count:d} extension point definitions")
|
|
def step_defs_count(context: Context, count: int) -> None:
|
|
assert len(context.ep_definitions) == count, (
|
|
f"Expected {count}, got {len(context.ep_definitions)}"
|
|
)
|
|
|
|
|
|
@then("each definition should have a non-empty name")
|
|
def step_each_def_has_name(context: Context) -> None:
|
|
for ep in context.ep_definitions:
|
|
assert ep.name, f"Extension point has empty name: {ep}"
|
|
|
|
|
|
@then("each definition should have a protocol_type")
|
|
def step_each_def_has_protocol(context: Context) -> None:
|
|
for ep in context.ep_definitions:
|
|
assert ep.protocol_type is not None, (
|
|
f"Extension point {ep.name} has no protocol_type"
|
|
)
|
|
|
|
|
|
@then("each definition should have a non-empty description")
|
|
def step_each_def_has_description(context: Context) -> None:
|
|
for ep in context.ep_definitions:
|
|
assert ep.description, f"Extension point {ep.name} has empty description"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lookup by name
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I look up extension point "{name}"')
|
|
def step_lookup_by_name(context: Context, name: str) -> None:
|
|
eps = context.ep_manager.list_extension_points()
|
|
matches = [ep for ep in eps if ep.name == name]
|
|
context.ep_lookup_result: ExtensionPoint | None = matches[0] if matches else None
|
|
|
|
|
|
@then('the looked-up extension point name should be "{name}"')
|
|
def step_lookup_name_is(context: Context, name: str) -> None:
|
|
assert context.ep_lookup_result is not None, f"Extension point '{name}' not found"
|
|
assert context.ep_lookup_result.name == name
|
|
|
|
|
|
@then("the looked-up extension point should be None")
|
|
def step_lookup_is_none(context: Context) -> None:
|
|
assert context.ep_lookup_result is None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Lookup by category
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I retrieve extension points grouped by category")
|
|
def step_grouped_by_category(context: Context) -> None:
|
|
context.ep_categories = get_extension_points_by_category()
|
|
|
|
|
|
@then('the "{category}" category should have {count:d} extension points')
|
|
def step_category_count(context: Context, category: str, count: int) -> None:
|
|
actual = len(context.ep_categories.get(category, []))
|
|
assert actual == count, f"Category '{category}': expected {count}, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Protocol type correctness
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('the protocol type name should be "{proto_name}"')
|
|
def step_protocol_type_name(context: Context, proto_name: str) -> None:
|
|
assert context.ep_lookup_result is not None
|
|
actual = context.ep_lookup_result.protocol_type.__name__
|
|
assert actual == proto_name, f"Expected '{proto_name}', got '{actual}'"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Entry point discovery
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I discover extension point plugins from group "{group}"')
|
|
def step_discover_ep_group(context: Context, group: str) -> None:
|
|
context.ep_discovered = context.ep_manager.discover(group=group)
|
|
|
|
|
|
@then("the entry point discovered list should be empty")
|
|
def step_ep_discovered_empty(context: Context) -> None:
|
|
assert len(context.ep_discovered) == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TOTAL_EXTENSION_POINTS constant
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("the TOTAL_EXTENSION_POINTS constant should equal {value:d}")
|
|
def step_total_constant(context: Context, value: int) -> None:
|
|
assert value == TOTAL_EXTENSION_POINTS, (
|
|
f"Expected {value}, got {TOTAL_EXTENSION_POINTS}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Context extension point wiring in ACMSPipeline
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("an ACMSPipeline wired with the plugin manager")
|
|
def step_pipeline_with_pm(context: Context) -> None:
|
|
from cleveragents.application.services.acms_service import ACMSPipeline
|
|
|
|
context.ep_pipeline = ACMSPipeline(
|
|
plugin_manager=context.ep_manager,
|
|
)
|
|
|
|
|
|
@given("an ACMSPipeline without a plugin manager")
|
|
def step_pipeline_without_pm(context: Context) -> None:
|
|
from cleveragents.application.services.acms_service import ACMSPipeline
|
|
|
|
context.ep_pipeline = ACMSPipeline()
|
|
|
|
|
|
@then("the pipeline should report {count:d} context extension points")
|
|
def step_pipeline_context_eps(context: Context, count: int) -> None:
|
|
actual = len(context.ep_pipeline.context_extension_points)
|
|
assert actual == count, f"Expected {count} context extension points, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Runtime-checkable protocols
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I retrieve all extension protocol types")
|
|
def step_retrieve_all_protocols(context: Context) -> None:
|
|
context.ep_protocol_map = ALL_EXTENSION_PROTOCOLS
|
|
|
|
|
|
@then("every protocol should be runtime-checkable")
|
|
def step_all_protocols_runtime_checkable(context: Context) -> None:
|
|
for name, proto in context.ep_protocol_map.items():
|
|
# runtime_checkable protocols have _is_runtime_protocol = True
|
|
is_rc: Any = getattr(proto, "_is_runtime_protocol", False)
|
|
assert is_rc, f"Protocol {name} ({proto.__name__}) is not runtime-checkable"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# registry_key matches category
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("each definition registry_key should match its name category prefix")
|
|
def step_registry_key_matches(context: Context) -> None:
|
|
for ep in context.ep_definitions:
|
|
expected_category = ep.name.split(".")[0]
|
|
assert ep.registry_key == expected_category, (
|
|
f"Extension point '{ep.name}': registry_key '{ep.registry_key}' "
|
|
f"does not match expected category '{expected_category}'"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Concrete protocol implementations for coverage
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _StubContextStrategy:
|
|
@property
|
|
def name(self) -> str:
|
|
return "stub"
|
|
|
|
def can_handle(self, request: Any) -> float:
|
|
return 1.0
|
|
|
|
def assemble(self, fragments: Any, budget: Any) -> list[Any]:
|
|
return []
|
|
|
|
|
|
class _StubPipelineComponent:
|
|
@property
|
|
def component_name(self) -> str:
|
|
return "stub_component"
|
|
|
|
def process(self, fragments: Any, context: Any) -> list[Any]:
|
|
return list(fragments)
|
|
|
|
|
|
class _StubStorageBackend:
|
|
def __init__(self) -> None:
|
|
self._store: dict[str, Any] = {}
|
|
|
|
@property
|
|
def backend_type(self) -> str:
|
|
return "memory"
|
|
|
|
def store(self, key: str, data: Any) -> None:
|
|
self._store[key] = data
|
|
|
|
def retrieve(self, key: str) -> Any:
|
|
return self._store.get(key)
|
|
|
|
|
|
class _StubOutputRenderer:
|
|
@property
|
|
def format_name(self) -> str:
|
|
return "text"
|
|
|
|
def render(self, data: Any) -> str:
|
|
return str(data)
|
|
|
|
|
|
class _StubOutputMaterializer:
|
|
@property
|
|
def target_type(self) -> str:
|
|
return "stdout"
|
|
|
|
def materialize(self, content: str, destination: str) -> None:
|
|
pass
|
|
|
|
|
|
class _StubOutputFormat:
|
|
@property
|
|
def format_id(self) -> str:
|
|
return "custom"
|
|
|
|
def serialize(self, data: Any) -> str:
|
|
return str(data)
|
|
|
|
def deserialize(self, raw: str) -> Any:
|
|
return raw
|
|
|
|
|
|
class _StubValidationRunner:
|
|
@property
|
|
def runner_name(self) -> str:
|
|
return "stub_runner"
|
|
|
|
def validate(self, target: Any) -> list[Any]:
|
|
return []
|
|
|
|
|
|
class _StubValidationRuleProvider:
|
|
@property
|
|
def provider_name(self) -> str:
|
|
return "stub_provider"
|
|
|
|
def get_rules(self) -> list[Any]:
|
|
return []
|
|
|
|
|
|
class _StubToolProvider:
|
|
@property
|
|
def provider_name(self) -> str:
|
|
return "stub_tools"
|
|
|
|
def list_tools(self) -> list[str]:
|
|
return ["tool1"]
|
|
|
|
def invoke(self, tool_name: str, arguments: Any) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubToolMiddleware:
|
|
@property
|
|
def middleware_name(self) -> str:
|
|
return "stub_mw"
|
|
|
|
def before_invoke(self, tool_name: str, arguments: Any) -> dict[str, Any]:
|
|
return dict(arguments)
|
|
|
|
def after_invoke(self, tool_name: str, result: Any) -> Any:
|
|
return result
|
|
|
|
|
|
class _StubSkillProvider:
|
|
@property
|
|
def provider_name(self) -> str:
|
|
return "stub_skills"
|
|
|
|
def list_skills(self) -> list[str]:
|
|
return ["skill1"]
|
|
|
|
def get_skill(self, skill_name: str) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubSkillTemplate:
|
|
@property
|
|
def template_name(self) -> str:
|
|
return "stub_template"
|
|
|
|
def render(self, context: Any) -> str:
|
|
return "rendered"
|
|
|
|
|
|
class _StubResourceResolver:
|
|
@property
|
|
def resolver_name(self) -> str:
|
|
return "stub_resolver"
|
|
|
|
def resolve(self, uri: str) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubResourceTypeHandler:
|
|
@property
|
|
def handled_type(self) -> str:
|
|
return "file"
|
|
|
|
def can_handle(self, resource_type: str) -> bool:
|
|
return resource_type == "file"
|
|
|
|
def load(self, uri: str) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubA2ATransport:
|
|
@property
|
|
def transport_name(self) -> str:
|
|
return "stub_transport"
|
|
|
|
def send(self, destination: str, payload: Any) -> None:
|
|
pass
|
|
|
|
def receive(self) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubA2AExtensionMethod:
|
|
@property
|
|
def method_name(self) -> str:
|
|
return "stub_method"
|
|
|
|
def execute(self, params: Any) -> Any:
|
|
return None
|
|
|
|
|
|
class _StubEventHandler:
|
|
@property
|
|
def handler_name(self) -> str:
|
|
return "stub_handler"
|
|
|
|
def handle(self, event: Any) -> None:
|
|
pass
|
|
|
|
|
|
class _StubEventFilter:
|
|
@property
|
|
def filter_name(self) -> str:
|
|
return "stub_filter"
|
|
|
|
def should_propagate(self, event: Any) -> bool:
|
|
return True
|
|
|
|
|
|
class _StubConfigSource:
|
|
@property
|
|
def source_name(self) -> str:
|
|
return "stub_source"
|
|
|
|
def load(self) -> dict[str, Any]:
|
|
return {}
|
|
|
|
|
|
class _StubConfigValidator:
|
|
@property
|
|
def validator_name(self) -> str:
|
|
return "stub_validator"
|
|
|
|
def validate(self, config: Any) -> list[str]:
|
|
return []
|
|
|
|
|
|
class _StubSafetyGuardrail:
|
|
@property
|
|
def guardrail_name(self) -> str:
|
|
return "stub_guardrail"
|
|
|
|
def check(self, action: Any) -> bool:
|
|
return True
|
|
|
|
def explain(self) -> str:
|
|
return "Always allows"
|
|
|
|
|
|
# --- Context Strategy ---
|
|
|
|
|
|
@given("a concrete ContextStrategyExtension implementation")
|
|
def step_concrete_ctx_strategy(context: Context) -> None:
|
|
context.ep_impl = _StubContextStrategy()
|
|
|
|
|
|
@then("it should be recognized as a ContextStrategyExtension instance")
|
|
def step_isinstance_ctx_strategy(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ContextStrategyExtension)
|
|
|
|
|
|
@then("calling its protocol methods should succeed")
|
|
def step_call_ctx_strategy_methods(context: Context) -> None:
|
|
impl = context.ep_impl
|
|
assert impl.name == "stub"
|
|
assert impl.can_handle({}) == 1.0
|
|
assert impl.assemble([], None) == []
|
|
|
|
|
|
# --- Pipeline Component ---
|
|
|
|
|
|
@given("a concrete ContextPipelineComponentExtension implementation")
|
|
def step_concrete_pipeline(context: Context) -> None:
|
|
context.ep_impl = _StubPipelineComponent()
|
|
|
|
|
|
@then("it should be recognized as a ContextPipelineComponentExtension instance")
|
|
def step_isinstance_pipeline(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ContextPipelineComponentExtension)
|
|
|
|
|
|
@then("calling its pipeline component methods should succeed")
|
|
def step_call_pipeline_methods(context: Context) -> None:
|
|
impl = context.ep_impl
|
|
assert impl.component_name == "stub_component"
|
|
assert impl.process(["a", "b"], {}) == ["a", "b"]
|
|
|
|
|
|
# --- Storage Backend ---
|
|
|
|
|
|
@given("a concrete ContextStorageBackendExtension implementation")
|
|
def step_concrete_storage(context: Context) -> None:
|
|
context.ep_impl = _StubStorageBackend()
|
|
|
|
|
|
@then("it should be recognized as a ContextStorageBackendExtension instance")
|
|
def step_isinstance_storage(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ContextStorageBackendExtension)
|
|
|
|
|
|
@then("calling its storage methods should succeed")
|
|
def step_call_storage_methods(context: Context) -> None:
|
|
impl = context.ep_impl
|
|
assert impl.backend_type == "memory"
|
|
impl.store("k", "v")
|
|
assert impl.retrieve("k") == "v"
|
|
|
|
|
|
# --- Output Renderer ---
|
|
|
|
|
|
@given("a concrete OutputRendererExtension implementation")
|
|
def step_concrete_renderer(context: Context) -> None:
|
|
context.ep_impl = _StubOutputRenderer()
|
|
|
|
|
|
@then("it should be recognized as an OutputRendererExtension instance")
|
|
def step_isinstance_renderer(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, OutputRendererExtension)
|
|
|
|
|
|
@then("calling its renderer methods should succeed")
|
|
def step_call_renderer_methods(context: Context) -> None:
|
|
impl = context.ep_impl
|
|
assert impl.format_name == "text"
|
|
assert impl.render(42) == "42"
|
|
|
|
|
|
# --- Output Materializer ---
|
|
|
|
|
|
@given("a concrete OutputMaterializerExtension implementation")
|
|
def step_concrete_materializer(context: Context) -> None:
|
|
context.ep_impl = _StubOutputMaterializer()
|
|
|
|
|
|
@then("it should be recognized as an OutputMaterializerExtension instance")
|
|
def step_isinstance_materializer(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, OutputMaterializerExtension)
|
|
|
|
|
|
# --- Output Format ---
|
|
|
|
|
|
@given("a concrete OutputFormatExtension implementation")
|
|
def step_concrete_format(context: Context) -> None:
|
|
context.ep_impl = _StubOutputFormat()
|
|
|
|
|
|
@then("it should be recognized as an OutputFormatExtension instance")
|
|
def step_isinstance_format(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, OutputFormatExtension)
|
|
|
|
|
|
# --- Validation Runner ---
|
|
|
|
|
|
@given("a concrete ValidationRunnerExtension implementation")
|
|
def step_concrete_val_runner(context: Context) -> None:
|
|
context.ep_impl = _StubValidationRunner()
|
|
|
|
|
|
@then("it should be recognized as a ValidationRunnerExtension instance")
|
|
def step_isinstance_val_runner(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ValidationRunnerExtension)
|
|
|
|
|
|
# --- Validation Rule Provider ---
|
|
|
|
|
|
@given("a concrete ValidationRuleProviderExtension implementation")
|
|
def step_concrete_val_rule(context: Context) -> None:
|
|
context.ep_impl = _StubValidationRuleProvider()
|
|
|
|
|
|
@then("it should be recognized as a ValidationRuleProviderExtension instance")
|
|
def step_isinstance_val_rule(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ValidationRuleProviderExtension)
|
|
|
|
|
|
# --- Tool Provider ---
|
|
|
|
|
|
@given("a concrete ToolProviderExtension implementation")
|
|
def step_concrete_tool_provider(context: Context) -> None:
|
|
context.ep_impl = _StubToolProvider()
|
|
|
|
|
|
@then("it should be recognized as a ToolProviderExtension instance")
|
|
def step_isinstance_tool_provider(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ToolProviderExtension)
|
|
|
|
|
|
# --- Tool Middleware ---
|
|
|
|
|
|
@given("a concrete ToolMiddlewareExtension implementation")
|
|
def step_concrete_tool_mw(context: Context) -> None:
|
|
context.ep_impl = _StubToolMiddleware()
|
|
|
|
|
|
@then("it should be recognized as a ToolMiddlewareExtension instance")
|
|
def step_isinstance_tool_mw(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ToolMiddlewareExtension)
|
|
|
|
|
|
# --- Skill Provider ---
|
|
|
|
|
|
@given("a concrete SkillProviderExtension implementation")
|
|
def step_concrete_skill_provider(context: Context) -> None:
|
|
context.ep_impl = _StubSkillProvider()
|
|
|
|
|
|
@then("it should be recognized as a SkillProviderExtension instance")
|
|
def step_isinstance_skill_provider(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, SkillProviderExtension)
|
|
|
|
|
|
# --- Skill Template ---
|
|
|
|
|
|
@given("a concrete SkillTemplateExtension implementation")
|
|
def step_concrete_skill_template(context: Context) -> None:
|
|
context.ep_impl = _StubSkillTemplate()
|
|
|
|
|
|
@then("it should be recognized as a SkillTemplateExtension instance")
|
|
def step_isinstance_skill_template(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, SkillTemplateExtension)
|
|
|
|
|
|
# --- Resource Resolver ---
|
|
|
|
|
|
@given("a concrete ResourceResolverExtension implementation")
|
|
def step_concrete_res_resolver(context: Context) -> None:
|
|
context.ep_impl = _StubResourceResolver()
|
|
|
|
|
|
@then("it should be recognized as a ResourceResolverExtension instance")
|
|
def step_isinstance_res_resolver(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ResourceResolverExtension)
|
|
|
|
|
|
# --- Resource Type Handler ---
|
|
|
|
|
|
@given("a concrete ResourceTypeHandlerExtension implementation")
|
|
def step_concrete_res_handler(context: Context) -> None:
|
|
context.ep_impl = _StubResourceTypeHandler()
|
|
|
|
|
|
@then("it should be recognized as a ResourceTypeHandlerExtension instance")
|
|
def step_isinstance_res_handler(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ResourceTypeHandlerExtension)
|
|
|
|
|
|
# --- A2A Transport ---
|
|
|
|
|
|
@given("a concrete A2ATransportExtension implementation")
|
|
def step_concrete_a2a_transport(context: Context) -> None:
|
|
context.ep_impl = _StubA2ATransport()
|
|
|
|
|
|
@then("it should be recognized as an A2ATransportExtension instance")
|
|
def step_isinstance_a2a_transport(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, A2ATransportExtension)
|
|
|
|
|
|
# --- A2A Extension Method ---
|
|
|
|
|
|
@given("a concrete A2AExtensionMethodExtension implementation")
|
|
def step_concrete_a2a_method(context: Context) -> None:
|
|
context.ep_impl = _StubA2AExtensionMethod()
|
|
|
|
|
|
@then("it should be recognized as an A2AExtensionMethodExtension instance")
|
|
def step_isinstance_a2a_method(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, A2AExtensionMethodExtension)
|
|
|
|
|
|
# --- Event Handler ---
|
|
|
|
|
|
@given("a concrete EventHandlerExtension implementation")
|
|
def step_concrete_event_handler(context: Context) -> None:
|
|
context.ep_impl = _StubEventHandler()
|
|
|
|
|
|
@then("it should be recognized as an EventHandlerExtension instance")
|
|
def step_isinstance_event_handler(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, EventHandlerExtension)
|
|
|
|
|
|
# --- Event Filter ---
|
|
|
|
|
|
@given("a concrete EventFilterExtension implementation")
|
|
def step_concrete_event_filter(context: Context) -> None:
|
|
context.ep_impl = _StubEventFilter()
|
|
|
|
|
|
@then("it should be recognized as an EventFilterExtension instance")
|
|
def step_isinstance_event_filter(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, EventFilterExtension)
|
|
|
|
|
|
# --- Config Source ---
|
|
|
|
|
|
@given("a concrete ConfigSourceExtension implementation")
|
|
def step_concrete_config_source(context: Context) -> None:
|
|
context.ep_impl = _StubConfigSource()
|
|
|
|
|
|
@then("it should be recognized as a ConfigSourceExtension instance")
|
|
def step_isinstance_config_source(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ConfigSourceExtension)
|
|
|
|
|
|
# --- Config Validator ---
|
|
|
|
|
|
@given("a concrete ConfigValidatorExtension implementation")
|
|
def step_concrete_config_validator(context: Context) -> None:
|
|
context.ep_impl = _StubConfigValidator()
|
|
|
|
|
|
@then("it should be recognized as a ConfigValidatorExtension instance")
|
|
def step_isinstance_config_validator(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, ConfigValidatorExtension)
|
|
|
|
|
|
# --- Safety Guardrail ---
|
|
|
|
|
|
@given("a concrete SafetyGuardrailExtension implementation")
|
|
def step_concrete_safety(context: Context) -> None:
|
|
context.ep_impl = _StubSafetyGuardrail()
|
|
|
|
|
|
@then("it should be recognized as a SafetyGuardrailExtension instance")
|
|
def step_isinstance_safety(context: Context) -> None:
|
|
assert isinstance(context.ep_impl, SafetyGuardrailExtension)
|
|
|
|
|
|
@then("calling its guardrail methods should succeed")
|
|
def step_call_safety_methods(context: Context) -> None:
|
|
impl = context.ep_impl
|
|
assert impl.guardrail_name == "stub_guardrail"
|
|
assert impl.check("some_action") is True
|
|
assert impl.explain() == "Always allows"
|