Files
temp/features/steps/server_cli_coverage_boost_steps.py
T
freemo a808c395f9 test(coverage): add Behave BDD tests to improve unit test coverage across 53 source modules
Add 53 new .feature files and corresponding step definition files targeting
uncovered lines identified in build/coverage.xml. Fix AmbiguousStep conflicts
in 7 pre-existing step files by disambiguating step text.

New tests cover: ACP clients/facade, actor CLI/config, application container,
ACMS service/strategies, async worker, automation profile CLI, autonomy
guardrail, bridge, change model, config CLI/service, context service,
cross-plan correction, database models, decision service, decomposition
clustering/service, discovery handler, langchain chat provider, langgraph
nodes, materializers, multi-project service, plan apply/CLI/lifecycle/model/
preflight/resume/service, PostgreSQL analyzer, project CLI/context CLI,
provider registry, reactive application/route, repositories, resolver handler,
resource registry service, resume model, retry patterns, sandbox protocol,
server CLI, skill CLI/service, skills registry, subplan execution/service,
system CLI, UKO loader, UoW, and YAML template engine.

Closes #645
2026-03-09 13:01:58 -04:00

283 lines
9.1 KiB
Python

"""Step definitions for server CLI coverage boost scenarios.
Targets uncovered lines in src/cleveragents/cli/commands/server.py:
- Lines 58-59: resolve_server_mode exception branches
- Lines 132-142: server_connect rich format Panel rendering
- Lines 157-204: server_status command (entire function)
"""
from __future__ import annotations
from io import StringIO
from typing import Any
from unittest.mock import MagicMock, patch
from behave import given, then, when
from behave.runner import Context
from rich.console import Console
from cleveragents.application.services.config_service import (
ConfigLevel,
ResolvedValue,
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_resolved(key: str, value: Any) -> ResolvedValue:
"""Build a ResolvedValue with DEFAULT source."""
return ResolvedValue(key=key, value=value, source=ConfigLevel.DEFAULT)
def _build_mock_svc(
resolve_map: dict[str, Any] | None = None,
raise_map: dict[str, type[Exception]] | None = None,
) -> MagicMock:
"""Build a mock ConfigService whose resolve() returns controlled values.
Parameters
----------
resolve_map:
``{key: value}`` — the mock returns a ``ResolvedValue`` with this value.
raise_map:
``{key: ExceptionClass}`` — the mock raises the given exception.
"""
resolve_map = resolve_map or {}
raise_map = raise_map or {}
svc = MagicMock()
def _resolve(key: str, **_kw: Any) -> ResolvedValue:
if key in raise_map:
raise raise_map[key](f"mock error for {key}")
val = resolve_map.get(key)
return _make_resolved(key, val)
svc.resolve.side_effect = _resolve
svc.read_config.return_value = {}
svc.write_config.return_value = None
return svc
# ---------------------------------------------------------------------------
# Given — mock config services
# ---------------------------------------------------------------------------
@given("a mock config service that raises ValueError on resolve")
def step_mock_svc_valueerror(context: Context) -> None:
context.mock_svc = _build_mock_svc(raise_map={"server.url": ValueError})
@given("a mock config service that raises KeyError on resolve")
def step_mock_svc_keyerror(context: Context) -> None:
context.mock_svc = _build_mock_svc(raise_map={"server.url": KeyError})
@given("a mock config service that accepts writes")
def step_mock_svc_writes(context: Context) -> None:
context.mock_svc = _build_mock_svc()
@given("a mock config service that resolves with no server config")
def step_mock_svc_no_config(context: Context) -> None:
context.mock_svc = _build_mock_svc(
resolve_map={
"server.url": None,
"server.namespace": None,
"server.tls-verify": None,
},
)
@given("a mock config service that resolves with full server config")
def step_mock_svc_full_config(context: Context) -> None:
context.mock_svc = _build_mock_svc(
resolve_map={
"server.url": "https://configured.example.com",
"server.namespace": "production",
"server.tls-verify": False,
},
)
@given("a mock config service where namespace resolve raises ValueError")
def step_mock_svc_ns_error(context: Context) -> None:
context.mock_svc = _build_mock_svc(
resolve_map={
"server.url": "https://ns-error.example.com",
"server.tls-verify": True,
},
raise_map={"server.namespace": ValueError},
)
@given("a mock config service where tls-verify resolve raises KeyError")
def step_mock_svc_tls_error(context: Context) -> None:
context.mock_svc = _build_mock_svc(
resolve_map={
"server.url": "https://tls-error.example.com",
"server.namespace": "default",
},
raise_map={"server.tls-verify": KeyError},
)
@given("a mock config service where all resolves raise exceptions")
def step_mock_svc_all_errors(context: Context) -> None:
context.mock_svc = _build_mock_svc(
raise_map={
"server.url": ValueError,
"server.namespace": KeyError,
"server.tls-verify": ValueError,
},
)
# ---------------------------------------------------------------------------
# When — resolve_server_mode
# ---------------------------------------------------------------------------
@when("I call resolve_server_mode with the mock")
def step_call_resolve_mode(context: Context) -> None:
from cleveragents.cli.commands.server import resolve_server_mode
with patch(
"cleveragents.cli.commands.server._get_config_service",
return_value=context.mock_svc,
):
context.server_mode_result = resolve_server_mode()
# ---------------------------------------------------------------------------
# When — server_connect with rich format
# ---------------------------------------------------------------------------
@when('I call server_connect with url "{url}" and rich format')
def step_call_connect_rich(context: Context, url: str) -> None:
from cleveragents.cli.commands.server import server_connect
buf = StringIO()
capture_console = Console(file=buf, width=200, no_color=True)
with (
patch(
"cleveragents.cli.commands.server._get_config_service",
return_value=context.mock_svc,
),
patch(
"cleveragents.cli.commands.server.console",
capture_console,
),
):
server_connect(server_url=url, namespace="default", tls_verify=True, fmt="rich")
context.captured_console_output = buf.getvalue()
@when(
'I call server_connect with url "{url}" namespace "{ns}" tls_verify false and rich format'
)
def step_call_connect_rich_ns_tls(context: Context, url: str, ns: str) -> None:
from cleveragents.cli.commands.server import server_connect
buf = StringIO()
capture_console = Console(file=buf, width=200, no_color=True)
with (
patch(
"cleveragents.cli.commands.server._get_config_service",
return_value=context.mock_svc,
),
patch(
"cleveragents.cli.commands.server.console",
capture_console,
),
):
server_connect(server_url=url, namespace=ns, tls_verify=False, fmt="rich")
context.captured_console_output = buf.getvalue()
# ---------------------------------------------------------------------------
# When — server_status
# ---------------------------------------------------------------------------
@when('I call server_status with format "{fmt}"')
def step_call_status(context: Context, fmt: str) -> None:
from cleveragents.cli.commands.server import server_status
echo_buf = StringIO()
console_buf = StringIO()
capture_console = Console(file=console_buf, width=200, no_color=True)
def _capture_echo(msg: str = "", **_kw: Any) -> None:
echo_buf.write(str(msg))
echo_buf.write("\n")
# We need to also patch resolve_server_mode since it calls _get_config_service
# internally. We compute the expected mode from the mock_svc.
mock_svc = context.mock_svc
# Determine what resolve_server_mode would return given this mock
try:
rv = mock_svc.resolve("server.url")
if rv.value is not None and str(rv.value).strip():
expected_mode = "stubbed"
else:
expected_mode = "disabled"
except (ValueError, KeyError):
expected_mode = "disabled"
with (
patch(
"cleveragents.cli.commands.server._get_config_service",
return_value=mock_svc,
),
patch(
"cleveragents.cli.commands.server.resolve_server_mode",
return_value=expected_mode,
),
patch(
"cleveragents.cli.commands.server.typer.echo",
side_effect=_capture_echo,
),
patch(
"cleveragents.cli.commands.server.console",
capture_console,
),
):
server_status(fmt=fmt)
context.echoed_output = echo_buf.getvalue()
context.captured_console_output = console_buf.getvalue()
# ---------------------------------------------------------------------------
# Then — assertions
# ---------------------------------------------------------------------------
@then('the server mode should be "{expected}"')
def step_assert_mode(context: Context, expected: str) -> None:
assert context.server_mode_result == expected, (
f"Expected mode '{expected}', got '{context.server_mode_result}'"
)
@then('the captured console output should contain "{text}"')
def step_assert_console_contains(context: Context, text: str) -> None:
output = getattr(context, "captured_console_output", "")
assert text in output, f"Expected '{text}' in console output:\n{output!r}"
@then('the echoed output should contain "{text}"')
def step_assert_echo_contains(context: Context, text: str) -> None:
output = getattr(context, "echoed_output", "")
assert text.lower() in output.lower(), (
f"Expected '{text}' in echoed output:\n{output!r}"
)