"""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