"""Helper script for validation_attach_named_options.robot E2E tests. Each subcommand is a self-contained check that prints a sentinel on success. Tests that ``agents validation attach`` accepts ``--key value`` named option format for extra validation arguments, as required by the spec. """ from __future__ import annotations import sys from collections.abc import Callable from pathlib import Path from unittest.mock import MagicMock, patch # Ensure local source tree is importable _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from helpers_common import reset_global_state # noqa: E402 from typer.testing import CliRunner # noqa: E402 from cleveragents.cli.commands.validation import app as validation_app # noqa: E402 runner = CliRunner() _ATTACHMENT_ULID = "01NAMEDOPT000000000000001" def _make_mock_attachment(validation_name: str, resource_id: str) -> MagicMock: """Create a mock attachment object.""" mock_attachment = MagicMock() mock_attachment.attachment_id = _ATTACHMENT_ULID mock_attachment.validation_name = validation_name mock_attachment.resource_id = resource_id mock_attachment.mode = "required" mock_attachment.project_name = None mock_attachment.plan_id = None mock_attachment.created_at = "2026-01-01T00:00:00" return mock_attachment # --------------------------------------------------------------------------- # Subcommands # --------------------------------------------------------------------------- def attach_with_coverage_threshold() -> None: """Verify that --coverage-threshold 90 named option is accepted and forwarded.""" mock_svc = MagicMock() mock_svc.attach_validation.return_value = _make_mock_attachment( "local/coverage-check", "git-checkout/my-repo" ) with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=mock_svc, ): result = runner.invoke( validation_app, [ "attach", "git-checkout/my-repo", "local/coverage-check", "--coverage-threshold", "90", "--format", "plain", ], ) if result.exit_code != 0: print( f"FAIL: exit={result.exit_code} output={result.output!r} " f"exception={result.exception!r}" ) sys.exit(1) # Verify the service received the correct args dict call_kwargs = mock_svc.attach_validation.call_args if call_kwargs is None: print("FAIL: attach_validation was not called") sys.exit(1) args_dict = call_kwargs.kwargs.get("args") or {} if args_dict.get("coverage_threshold") != "90": print(f"FAIL: expected args={{'coverage_threshold': '90'}}, got {args_dict!r}") sys.exit(1) print("validation-attach-named-option-coverage-threshold-ok") def attach_with_multiple_named_options() -> None: """Verify that multiple --key value named options are all forwarded.""" mock_svc = MagicMock() mock_svc.attach_validation.return_value = _make_mock_attachment( "local/lint-check", "git-checkout/my-repo" ) with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=mock_svc, ): result = runner.invoke( validation_app, [ "attach", "git-checkout/my-repo", "local/lint-check", "--threshold", "70", "--strict", "true", "--format", "plain", ], ) if result.exit_code != 0: print( f"FAIL: exit={result.exit_code} output={result.output!r} " f"exception={result.exception!r}" ) sys.exit(1) call_kwargs = mock_svc.attach_validation.call_args if call_kwargs is None: print("FAIL: attach_validation was not called") sys.exit(1) args_dict = call_kwargs.kwargs.get("args") or {} if args_dict.get("threshold") != "70": print(f"FAIL: expected threshold='70', got {args_dict!r}") sys.exit(1) if args_dict.get("strict") != "true": print(f"FAIL: expected strict='true', got {args_dict!r}") sys.exit(1) print("validation-attach-named-option-multiple-ok") def attach_without_extra_args() -> None: """Verify that attach without extra named options still works.""" mock_svc = MagicMock() mock_svc.attach_validation.return_value = _make_mock_attachment( "local/basic-check", "git-checkout/my-repo" ) with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=mock_svc, ): result = runner.invoke( validation_app, [ "attach", "git-checkout/my-repo", "local/basic-check", "--format", "plain", ], ) if result.exit_code != 0: print( f"FAIL: exit={result.exit_code} output={result.output!r} " f"exception={result.exception!r}" ) sys.exit(1) print("validation-attach-named-option-no-extra-args-ok") def attach_with_positional_key_value_rejected() -> None: """Verify that old-style positional key=value format is rejected.""" mock_svc = MagicMock() mock_svc.attach_validation.return_value = _make_mock_attachment( "local/coverage-check", "git-checkout/my-repo" ) with patch( "cleveragents.cli.commands.validation._get_tool_registry_service", return_value=mock_svc, ): result = runner.invoke( validation_app, [ "attach", "git-checkout/my-repo", "local/coverage-check", "coverage-threshold=90", "--format", "plain", ], ) # Must be rejected (non-zero exit) and output must mention "Invalid argument format" if result.exit_code != 0 and "Invalid argument format" in result.output: print("validation-attach-positional-key-value-rejected-ok") else: print( f"FAIL: exit={result.exit_code} " f"output={result.output!r} " f"(expected non-zero exit and 'Invalid argument format' in output)" ) sys.exit(1) # --------------------------------------------------------------------------- # Dispatcher # --------------------------------------------------------------------------- _COMMANDS: dict[str, Callable[[], None]] = { "attach-with-coverage-threshold": attach_with_coverage_threshold, "attach-with-multiple-named-options": attach_with_multiple_named_options, "attach-without-extra-args": attach_without_extra_args, "attach-with-positional-key-value-rejected": ( attach_with_positional_key_value_rejected ), } if __name__ == "__main__": if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS: print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>") sys.exit(1) reset_global_state() fn = _COMMANDS[sys.argv[1]] fn()