3f5ca4e869
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 20s
CI / helm (pull_request) Successful in 22s
CI / lint (pull_request) Failing after 27s
CI / security (pull_request) Failing after 41s
CI / quality (pull_request) Successful in 3m41s
CI / typecheck (pull_request) Successful in 3m56s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / unit_tests (pull_request) Successful in 4m1s
CI / integration_tests (pull_request) Failing after 4m2s
CI / docker (pull_request) Has been skipped
CI / e2e_tests (pull_request) Successful in 20m59s
CI / status-check (pull_request) Failing after 1s
Registered all 30 spec-defined extension points in the ExtensionPointRegistry with typed Protocol interfaces for each category: context strategies (12), output renderers (3), validation runners (2), tool providers (2), skill providers (2), resource handlers (2), A2A transports (2), event handlers (2), config sources (2), and safety guardrails (1). Added extension_protocols.py with Protocol definitions and extension_catalog.py with the registration catalog. Wired context subsystem to query context.* extension points at initialization. ISSUES CLOSED: #939
788 lines
23 KiB
Python
788 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"
|