forked from HAL9000/cleveragents-core
5e96b4bf80
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
247 lines
8.3 KiB
Python
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}"
|
|
)
|