forked from HAL9000/cleveragents-core
4d31f0ed02
Add `fmt: OutputFormat` parameter to `main_callback()` in
`src/cleveragents/cli/main.py` and store the selected format in
`ctx.obj["format"]` so all subcommands can read it without needing
their own per-command `--format` flag.
Remove per-command `--format` / `fmt` parameters from `version()`,
`info()`, and `diagnostics()` commands. These commands now read the
format from `ctx.obj.get("format", OutputFormat.RICH.value)`.
The specification states: "The framework supports six distinct output
formats, selectable via the global `--format` flag." This change
aligns the implementation with the spec by making `--format` a global
option on the root `agents` command (via the Typer callback).
All six formats (json, yaml, plain, rich, table, color) are supported
via the global flag and the `-f` shorthand.
Add Behave BDD scenarios covering global `--format` flag propagation
to subcommands for all six formats. Update Robot Framework integration
tests to exercise the global `--format` flag. Update existing tests
that used per-command `--format` for version/info/diagnostics to use
the global flag instead.
ISSUES CLOSED: #2908
159 lines
5.4 KiB
Python
159 lines
5.4 KiB
Python
"""Step definitions for global --format flag propagation tests.
|
|
|
|
Tests that the --format flag is a global option on the root `agents` command
|
|
(via the Typer callback), not a per-command option, per the specification.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
from unittest.mock import patch
|
|
|
|
import yaml
|
|
from behave import given, then, when
|
|
from typer.testing import CliRunner
|
|
|
|
from cleveragents.cli.main import app
|
|
|
|
|
|
@given("the global format flag test runner is set up")
|
|
def step_setup_runner(context: Any) -> None:
|
|
"""Set up the test runner for global format flag tests."""
|
|
context.runner = CliRunner()
|
|
context.cli_result = None
|
|
context.ctx_obj = {}
|
|
|
|
|
|
def _invoke_with_mocked_system(runner: CliRunner, args: list[str]) -> Any:
|
|
"""Invoke the CLI app with mocked system commands."""
|
|
with (
|
|
patch(
|
|
"cleveragents.cli.commands.system.build_version_data",
|
|
return_value={
|
|
"version": "3.7.0",
|
|
"python": "3.12.0",
|
|
"platform": "linux",
|
|
},
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands.system.render_version_rich",
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands.system.build_info_data",
|
|
return_value={
|
|
"version": "3.7.0",
|
|
"config_path": "/tmp/config.toml",
|
|
"db_url": "sqlite:///test.db",
|
|
},
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands.system.render_info_rich",
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands.system.build_diagnostics_data",
|
|
return_value={
|
|
"checks": [],
|
|
"has_errors": False,
|
|
},
|
|
),
|
|
patch(
|
|
"cleveragents.cli.commands.system.render_diagnostics_rich",
|
|
),
|
|
):
|
|
return runner.invoke(app, args)
|
|
|
|
|
|
@when('I invoke the CLI with global args "{args_str}"')
|
|
def step_invoke_cli_global_args(context: Any, args_str: str) -> None:
|
|
"""Invoke the CLI with the given argument string."""
|
|
args = args_str.split()
|
|
result = _invoke_with_mocked_system(context.runner, args)
|
|
context.cli_result = result
|
|
context.cli_output = result.output
|
|
|
|
|
|
@then("the global format command should succeed")
|
|
def step_global_format_command_succeeds(context: Any) -> None:
|
|
"""Verify the command exited with code 0."""
|
|
result = context.cli_result
|
|
assert result.exit_code == 0, (
|
|
f"Expected exit code 0, got {result.exit_code}.\n"
|
|
f"Output: {result.output}\n"
|
|
f"Exception: {result.exception}"
|
|
)
|
|
|
|
|
|
@then("the global format output should be valid JSON")
|
|
def step_global_format_output_is_valid_json(context: Any) -> None:
|
|
"""Verify the output is parseable JSON."""
|
|
output = context.cli_output.strip()
|
|
try:
|
|
parsed = json.loads(output)
|
|
context.parsed_json = parsed
|
|
except json.JSONDecodeError as exc:
|
|
raise AssertionError(
|
|
f"Output is not valid JSON: {exc}\nOutput was: {output!r}"
|
|
) from exc
|
|
|
|
|
|
@then("the global format output should be valid YAML")
|
|
def step_global_format_output_is_valid_yaml(context: Any) -> None:
|
|
"""Verify the output is parseable YAML."""
|
|
output = context.cli_output.strip()
|
|
try:
|
|
parsed = yaml.safe_load(output)
|
|
context.parsed_yaml = parsed
|
|
except yaml.YAMLError as exc:
|
|
raise AssertionError(
|
|
f"Output is not valid YAML: {exc}\nOutput was: {output!r}"
|
|
) from exc
|
|
|
|
|
|
@then("the global format output should contain plain text")
|
|
def step_global_format_output_contains_plain_text(context: Any) -> None:
|
|
"""Verify the output contains plain key: value text."""
|
|
output = context.cli_output.strip()
|
|
assert output, f"Expected non-empty plain text output, got: {output!r}"
|
|
|
|
|
|
@then('the global format JSON output should contain key "{key}"')
|
|
def step_global_format_json_output_contains_key(context: Any, key: str) -> None:
|
|
"""Verify the parsed JSON output contains the given key."""
|
|
parsed = getattr(context, "parsed_json", None)
|
|
if parsed is None:
|
|
output = context.cli_output.strip()
|
|
parsed = json.loads(output)
|
|
assert key in parsed, (
|
|
f"Expected key '{key}' in JSON output. Keys found: {list(parsed.keys())}"
|
|
)
|
|
|
|
|
|
@when('I call main_callback directly with format "{fmt}"')
|
|
def step_call_main_callback_directly(context: Any, fmt: str) -> None:
|
|
"""Call main_callback via the CLI runner to test ctx.obj storage."""
|
|
result = _invoke_with_mocked_system(context.runner, ["--format", fmt, "version"])
|
|
context.cli_result = result
|
|
context.captured_fmt = fmt
|
|
|
|
|
|
@then('the context object should have format "{expected_fmt}"')
|
|
def step_ctx_obj_has_format(context: Any, expected_fmt: str) -> None:
|
|
"""Verify the format was stored correctly.
|
|
|
|
Since we can't easily inspect ctx.obj after the fact without modifying
|
|
the command, we verify indirectly: the command succeeded and the format
|
|
was accepted (no 'Invalid value' error for the format option).
|
|
"""
|
|
result = context.cli_result
|
|
# The command should succeed (format was accepted)
|
|
assert result.exit_code == 0, (
|
|
f"Expected exit code 0 for format '{expected_fmt}', "
|
|
f"got {result.exit_code}.\nOutput: {result.output}"
|
|
)
|
|
# Verify no "Invalid value" error for the format option
|
|
assert "Invalid value" not in result.output, (
|
|
f"Got 'Invalid value' error for format '{expected_fmt}': {result.output}"
|
|
)
|
|
context.verified_fmt = expected_fmt
|