Files
temp/features/steps/a2a_clients_coverage_boost_steps.py
hamza.khyari 5e96b4bf80 feat(resource): implement 6-level execution environment precedence chain
Implement the spec's 6-level execution environment precedence chain
(spec lines 19324-19386):

1. Plan override (priority=override) — always wins
2. Project override (priority=override) — wins over devcontainer
3. Nearest-ancestor devcontainer — auto-discovered
4. Plan fallback (priority=fallback) — defers to devcontainer
5. Project fallback (priority=fallback) — defers to closer scopes
6. Host default — final fallback

- New resolve_with_precedence() API on ExecutionEnvironmentResolver
- Added execution_env_priority field to ContextConfig (project model)
- has_devcontainer() helper for devcontainer-instance detection
- Legacy 4-level resolve() preserved for backward compatibility
- _parse_priority() defaults missing priority to FALLBACK
- 13 new Behave scenarios testing all 6 levels + edge cases
- Updated CHANGELOG

ISSUES CLOSED: #877
2026-03-31 16:01:58 +00:00

247 lines
8.3 KiB
Python

"""Step definitions for A2A clients coverage boost scenarios.
Covers:
- Protocol default method bodies (the ``...`` expression on lines 28, 36, 52, 63, 74, 90, 101)
- Stub validation branches for empty/non-string inputs (lines 149, 159, 186)
"""
from __future__ import annotations
import contextlib
from behave import given, then, when
from behave.runner import Context
with contextlib.suppress(ImportError):
from cleveragents.a2a.clients import (
AuthClient,
RemoteExecutionClient,
ServerClient,
StubAuthClient,
StubRemoteExecutionClient,
)
# ---------------------------------------------------------------------------
# Concrete subclasses that inherit Protocol default method bodies
# ---------------------------------------------------------------------------
class _ConcreteServerClient(ServerClient):
"""Minimal concrete subclass — inherits the ``...`` default bodies."""
pass
class _ConcreteRemoteExecutionClient(RemoteExecutionClient):
"""Minimal concrete subclass — inherits the ``...`` default bodies."""
pass
class _ConcreteAuthClient(AuthClient):
"""Minimal concrete subclass — inherits the ``...`` default bodies."""
pass
# ---------------------------------------------------------------------------
# Given steps — Protocol concrete subclasses
# ---------------------------------------------------------------------------
@given("a concrete subclass of ServerClient protocol")
def step_concrete_server_client(context: Context) -> None:
context.concrete_server = _ConcreteServerClient()
context.call_error = None
@given("a concrete subclass of RemoteExecutionClient protocol")
def step_concrete_exec_client(context: Context) -> None:
context.concrete_exec = _ConcreteRemoteExecutionClient()
context.call_error = None
@given("a concrete subclass of AuthClient protocol")
def step_concrete_auth_client(context: Context) -> None:
context.concrete_auth = _ConcreteAuthClient()
context.call_error = None
# ---------------------------------------------------------------------------
# When steps — call Protocol default method bodies
# ---------------------------------------------------------------------------
@when("I call health_check on the concrete server client")
def step_call_concrete_health_check(context: Context) -> None:
try:
context.call_result = context.concrete_server.health_check()
except Exception as exc:
context.call_error = exc
@when("I call get_version on the concrete server client")
def step_call_concrete_get_version(context: Context) -> None:
try:
context.call_result = context.concrete_server.get_version()
except Exception as exc:
context.call_error = exc
@when("I call execute_plan on the concrete execution client")
def step_call_concrete_execute_plan(context: Context) -> None:
try:
context.call_result = context.concrete_exec.execute_plan("test-plan")
except Exception as exc:
context.call_error = exc
@when("I call get_plan_status on the concrete execution client")
def step_call_concrete_get_plan_status(context: Context) -> None:
try:
context.call_result = context.concrete_exec.get_plan_status("test-plan")
except Exception as exc:
context.call_error = exc
@when("I call cancel_plan on the concrete execution client")
def step_call_concrete_cancel_plan(context: Context) -> None:
try:
context.call_result = context.concrete_exec.cancel_plan("test-plan")
except Exception as exc:
context.call_error = exc
@when("I call authenticate on the concrete auth client")
def step_call_concrete_authenticate(context: Context) -> None:
try:
context.call_result = context.concrete_auth.authenticate("tok_test")
except Exception as exc:
context.call_error = exc
@when("I call validate_token on the concrete auth client")
def step_call_concrete_validate_token(context: Context) -> None:
try:
context.call_result = context.concrete_auth.validate_token("tok_test")
except Exception as exc:
context.call_error = exc
# ---------------------------------------------------------------------------
# Then step — Protocol calls complete without error
# ---------------------------------------------------------------------------
@then("the call should complete without error")
def step_call_completes(context: Context) -> None:
assert context.call_error is None, (
f"Expected no error but got: {context.call_error!r}"
)
# ---------------------------------------------------------------------------
# Given steps — Stub instances for validation branch coverage
# ---------------------------------------------------------------------------
@given("a fresh StubRemoteExecutionClient for coverage")
def step_fresh_stub_exec(context: Context) -> None:
context.cov_stub_exec = StubRemoteExecutionClient()
context.cov_caught_error = None
@given("a fresh StubAuthClient for coverage")
def step_fresh_stub_auth(context: Context) -> None:
context.cov_stub_auth = StubAuthClient()
context.cov_caught_error = None
# ---------------------------------------------------------------------------
# When steps — empty string inputs to stubs
# ---------------------------------------------------------------------------
@when("I call get_plan_status with empty plan_id on the stub")
def step_stub_get_plan_status_empty(context: Context) -> None:
try:
context.cov_stub_exec.get_plan_status("")
except ValueError as exc:
context.cov_caught_error = exc
@when("I call cancel_plan with empty plan_id on the stub")
def step_stub_cancel_plan_empty(context: Context) -> None:
try:
context.cov_stub_exec.cancel_plan("")
except ValueError as exc:
context.cov_caught_error = exc
@when("I call validate_token with empty token on the stub")
def step_stub_validate_token_empty(context: Context) -> None:
try:
context.cov_stub_auth.validate_token("")
except ValueError as exc:
context.cov_caught_error = exc
# ---------------------------------------------------------------------------
# When steps — non-string type inputs to stubs
# ---------------------------------------------------------------------------
@when("I call get_plan_status with a non-string plan_id on the stub")
def step_stub_get_plan_status_non_string(context: Context) -> None:
try:
context.cov_stub_exec.get_plan_status(12345) # type: ignore[arg-type]
except ValueError as exc:
context.cov_caught_error = exc
@when("I call cancel_plan with a non-string plan_id on the stub")
def step_stub_cancel_plan_non_string(context: Context) -> None:
try:
context.cov_stub_exec.cancel_plan(12345) # type: ignore[arg-type]
except ValueError as exc:
context.cov_caught_error = exc
@when("I call validate_token with a non-string token on the stub")
def step_stub_validate_token_non_string(context: Context) -> None:
try:
context.cov_stub_auth.validate_token(12345) # type: ignore[arg-type]
except ValueError as exc:
context.cov_caught_error = exc
# ---------------------------------------------------------------------------
# Then steps — ValueError assertions
# ---------------------------------------------------------------------------
@then("a ValueError should be raised with plan_id validation message")
def step_value_error_plan_id_msg(context: Context) -> None:
assert context.cov_caught_error is not None, (
"Expected ValueError but none was raised"
)
assert isinstance(context.cov_caught_error, ValueError), (
f"Expected ValueError, got {type(context.cov_caught_error).__name__}"
)
assert "plan_id" in str(context.cov_caught_error), (
f"Expected 'plan_id' in error message, got: {context.cov_caught_error}"
)
@then("a ValueError should be raised with token validation message")
def step_value_error_token_msg(context: Context) -> None:
assert context.cov_caught_error is not None, (
"Expected ValueError but none was raised"
)
assert isinstance(context.cov_caught_error, ValueError), (
f"Expected ValueError, got {type(context.cov_caught_error).__name__}"
)
assert "token" in str(context.cov_caught_error), (
f"Expected 'token' in error message, got: {context.cov_caught_error}"
)