"""Step definitions for A2A facade coverage-boost scenarios. Targets uncovered lines in ``src/cleveragents/a2a/facade.py``: | Line | Code path | |------|--------------------------------------------------------| | 82 | TypeError when ``services`` is not a dict or None | | 256 | ValueError in plan.execute when plan_id is empty | | 266 | ValueError in plan.status when plan_id is empty | | 281 | ValueError in plan.diff when plan_id is empty | | 295 | ValueError in plan.apply when plan_id is empty | """ from __future__ import annotations import json from typing import Any from unittest.mock import MagicMock from behave import given, then, use_step_matcher, when from behave.runner import Context try: from cleveragents.a2a.facade import A2aLocalFacade from cleveragents.a2a.models import A2aRequest except ImportError: pass # a2a module not available from features.steps._a2a_code_map import A2A_CODE_MAP use_step_matcher("re") # --------------------------------------------------------------------------- # Mock helpers (mirrors wiring steps — kept local to avoid coupling) # --------------------------------------------------------------------------- class _MockPlanIdentity: def __init__(self, plan_id: str = "MOCK-PLAN-001") -> None: self.plan_id = plan_id class _MockPlan: def __init__(self, plan_id: str = "MOCK-PLAN-001") -> None: self.identity = _MockPlanIdentity(plan_id) self.phase = MagicMock() self.phase.value = "strategize" self.state = MagicMock() self.state.value = "queued" def _build_mock_plan_lifecycle_service() -> MagicMock: svc = MagicMock() svc.use_action.return_value = _MockPlan() svc.execute_plan.return_value = _MockPlan() svc.get_plan.return_value = _MockPlan() svc.apply_plan.return_value = _MockPlan() return svc # --------------------------------------------------------------------------- # Given steps # --------------------------------------------------------------------------- @given(r"a coverage-boost facade with a mock PlanLifecycleService") def step_cb_facade_plan(context: Context) -> None: context.cb_facade = A2aLocalFacade( services={"plan_lifecycle_service": _build_mock_plan_lifecycle_service()} ) # --------------------------------------------------------------------------- # When steps # --------------------------------------------------------------------------- @when(r"I try to create an A2aLocalFacade with a non-dict services argument") def step_cb_create_facade_non_dict(context: Context) -> None: context.cb_caught_error = None try: A2aLocalFacade(services=["not", "a", "dict"]) # type: ignore[arg-type] except TypeError as exc: context.cb_caught_error = exc @when( r'I dispatch coverage-boost operation "(?P[^"]+)" ' r"with params (?P.+)" ) def step_cb_dispatch(context: Context, operation: str, params_json: str) -> None: params: dict[str, Any] = json.loads(params_json) request = A2aRequest(method=operation, params=params) context.cb_response = context.cb_facade.dispatch(request) # --------------------------------------------------------------------------- # Then steps # --------------------------------------------------------------------------- @then(r'a TypeError should be raised with message "(?P[^"]+)"') def step_cb_type_error_raised(context: Context, msg: str) -> None: assert isinstance(context.cb_caught_error, TypeError), ( f"Expected TypeError, got {type(context.cb_caught_error)}" ) assert msg in str(context.cb_caught_error), ( f"Expected message containing '{msg}', got '{context.cb_caught_error}'" ) @then(r'the coverage-boost response status should be "(?P[^"]+)"') def step_cb_response_status(context: Context, status: str) -> None: assert (context.cb_response.error is None) == (status == "ok"), ( f"Expected status '{status}', got error={context.cb_response.error}" ) @then(r'the coverage-boost response error code should be "(?P[^"]+)"') def step_cb_error_code(context: Context, code: str) -> None: expected_code: int = A2A_CODE_MAP.get( code, int(code) if code.lstrip("-").isdigit() else -1 ) assert context.cb_response.error is not None, "No error in response" assert context.cb_response.error.code == expected_code, ( f"Expected error code {expected_code} ('{code}'), " f"got '{context.cb_response.error.code}'" ) @then(r'the coverage-boost response error message should contain "(?P[^"]+)"') def step_cb_error_message_contains(context: Context, fragment: str) -> None: assert context.cb_response.error is not None, "No error in response" assert fragment in context.cb_response.error.message, ( f"Expected message containing '{fragment}', " f"got '{context.cb_response.error.message}'" ) # Reset step matcher to parse (default) so subsequent step files are not affected use_step_matcher("parse")