forked from HAL9000/cleveragents-core
02250473ad
Fix all failing CI quality gates (lint, unit_tests, format) without suppressing any quality enforcement. Root causes and fixes: 1. Format: features/steps/plan_namespaced_name_tdd_steps.py had trailing whitespace; fixed by running ruff format. 2. Unit tests - A2A JSON-RPC 2.0 migration (commit9c6d6915) renamed A2aRequest fields (operation→method, request_id→id, a2a_version→jsonrpc) and A2aResponse fields (status+data→result, request_id→id) but did not update all step files and feature files: - a2a_jsonrpc_wire_format_steps.py: added use_step_matcher('re') and reset to 'parse' at end to prevent parallel test interference - a2a_facade_wiring_steps.py: updated operation= to method=, .status/.data to .result - a2a_facade_steps.py: updated request_id→id, a2a_version→jsonrpc, A2aResponse(request_id=..., status=...) to new API - m6_facade_steps.py: updated all old API usage - devcontainer_cleanup_steps.py: updated A2aRequest(operation=...) - plan_prompt_command_steps.py: updated A2aRequest(operation=...) - wf03_plan_prompt_confidence_steps.py: updated A2aRequest(operation=...) - consolidated_misc.feature: updated old A2aRequest/A2aResponse scenarios 3. Unit tests - Session CLI output changed (commit0d5d9cf0and others): - 'Session Created' → 'Session created' (lowercase) - 'Session Details' → 'Session Summary' - 'Sessions (N total)' → 'Sessions' - session list JSON: top-level 'total' → nested 'summary.total' - Fixed in: session_cli.feature, session_cli_coverage_boost.feature, session_cli_uncovered_branches.feature, session_list_error.feature, tdd_session_create_persist_steps.py 4. Unit tests - Plan list output changed (commit1a07a891): - 'V3 Lifecycle Plans' → 'Plans' - 'Lifecycle Plans' → 'Plans' - Name column removed (restored in source) - Invariants column removed (restored in source) - Project truncation removed (restored in source) - Fixed in: plan_cli_cancel_revert_coverage.feature, plan_lifecycle_cli_coverage.feature, plan_cli_coverage_boost_steps.py, plan.py (source code restored) 5. Unit tests - Plan apply command now requires ULID (commit300a5d6d): - plan_cli_coverage_r3.feature: updated 'PLAN-001' to valid ULID - plan_cli_coverage_r3_steps.py: added --yes flag, added new step for no-eligible-plans path 6. Unit tests - Various source code bugs: - ThoughtBlock: converted from @dataclass to Pydantic BaseModel (architecture test requires all dataclasses to use Pydantic) - session.py: added DatabaseError handling to export, import, tell commands - database.py: fixed rollback_to() to reuse checkpoint connection for writes - database.py: added _get_checkpoint_conn() helper - check-tls-cert.py: fixed SSLCertVerificationError.reason AttributeError 7. Unit tests - Test step bugs: - error_recovery_coverage_boost_steps.py: fixed invalid ULID _PLAN_ID - session_service_coverage_steps.py: fixed 'sha256:' prefix bug in checksum - database_models_new_coverage_steps.py: added 'name' field to session mock - async_audit_recording_steps.py: fixed Settings(audit_async=False) via env var - coverage_threshold_config_steps.py: added --coverage-min pattern support - m5_acms_smoke_steps.py: updated usage hint text - actor_cli_yaml_steps.py: updated 'Removed actor' → 'Actor removed' - aimodelscredentials_steps.py: set context.imported_class in import step - domain_base_model.feature: added missing 'When I examine model_config' step - tui_first_run_steps.py: fixed module reload to restore cleveragents.tui.* modules after test (prevented patch interference in subsequent tests) - tui_first_run_steps.py: added set_search('') step for empty string - resource_handler_base_coverage_r3_steps.py: use _MinimalHandler instead of DatabaseResourceHandler for NotImplementedError tests - resource_handler_crud.feature: updated to test new DatabaseHandler behavior - resource_handler_sandbox.feature: updated to test new DatabaseHandler behavior - tdd_json_decode_crash_persistence.feature: fixed @tdd_bug → @tdd_issue tags 8. Parallel test interference: - All step files using use_step_matcher('re') now reset to 'parse' at end to prevent global matcher state leaking to subsequent step files
137 lines
4.8 KiB
Python
137 lines
4.8 KiB
Python
"""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
|
|
|
|
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<operation>[^"]+)" '
|
|
r"with params (?P<params_json>.+)"
|
|
)
|
|
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<msg>[^"]+)"')
|
|
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<status>[^"]+)"')
|
|
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<code>[^"]+)"')
|
|
def step_cb_error_code(context: Context, code: str) -> None:
|
|
assert context.cb_response.error is not None, "No error in response"
|
|
assert context.cb_response.error.code == code, (
|
|
f"Expected error code '{code}', got '{context.cb_response.error.code}'"
|
|
)
|
|
|
|
|
|
@then(r'the coverage-boost response error message should contain "(?P<fragment>[^"]+)"')
|
|
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")
|