"""Tests for the placeholder toolkit CLI and its request document.""" from __future__ import annotations import json import subprocess import sys from pathlib import Path import pytest from tools import example REPO_ROOT = Path(__file__).resolve().parents[1] EXAMPLE_REQUEST = REPO_ROOT / "examples" / "input.json" def run_cli(*args: str) -> subprocess.CompletedProcess[str]: """Run the CLI in a subprocess so stdout/exit codes are asserted end to end.""" return subprocess.run( [sys.executable, "-m", "tools.example", *args], cwd=REPO_ROOT, capture_output=True, text=True, check=False, ) def test_example_request_document_is_valid_json_object() -> None: document = json.loads(EXAMPLE_REQUEST.read_text(encoding="utf-8")) assert isinstance(document, dict) assert set(example.REQUIRED_FIELDS) <= set(document) assert document["dry_run"] is True assert document["mode"] == "validate" def test_example_request_document_passes_validation() -> None: request = example.validate_request(example.load_request(EXAMPLE_REQUEST)) assert request["mode"] == "validate" assert request["dry_run"] is True assert request["arguments"]["request"] == "examples/input.json" def test_cli_prints_single_json_envelope_and_exits_zero() -> None: result = run_cli("examples/input.json") assert result.returncode == 0, result.stderr assert result.stderr == "" lines = [line for line in result.stdout.splitlines() if line.strip()] assert len(lines) == 1 envelope = json.loads(lines[0]) assert envelope == { "status": "ok", "command": "example", "mode": "validate", "dry_run": True, "version": 1, "request": "examples/input.json", } def test_cli_help_exits_zero() -> None: result = run_cli("--help") assert result.returncode == 0 assert "REQUEST" in result.stdout def test_cli_reports_missing_request_document(tmp_path: Path) -> None: missing = tmp_path / "missing.json" result = run_cli(str(missing)) assert result.returncode == example.EXIT_INVALID_REQUEST envelope = json.loads(result.stdout) assert envelope["status"] == "error" assert envelope["error"]["code"] == "request_not_found" def test_cli_reports_malformed_json(tmp_path: Path) -> None: malformed = tmp_path / "malformed.json" malformed.write_text("{not json", encoding="utf-8") result = run_cli(str(malformed)) assert result.returncode == example.EXIT_INVALID_REQUEST assert json.loads(result.stdout)["error"]["code"] == "request_not_json" @pytest.mark.parametrize( ("document", "code"), [ ([], "request_not_object"), ({"mode": "validate", "dry_run": True, "command": "example", "arguments": {}}, "request_field_missing"), ({"version": 1, "mode": "validate", "dry_run": False, "command": "example", "arguments": {"request": "x"}}, "request_not_dry_run"), ({"version": 1, "mode": "execute", "dry_run": True, "command": "example", "arguments": {"request": "x"}}, "request_mode_unsupported"), ({"version": 1, "mode": "validate", "dry_run": True, "command": "example", "arguments": {}}, "request_field_missing"), ({"version": 1, "mode": "validate", "dry_run": True, "command": "", "arguments": {"request": "x"}}, "request_field_invalid"), ], ) def test_validate_request_rejects_invalid_documents(document: object, code: str) -> None: with pytest.raises(example.RequestError) as excinfo: example.validate_request(document) assert excinfo.value.code == code def test_envelope_is_deterministic() -> None: request = example.validate_request(example.load_request(EXAMPLE_REQUEST)) first = example.build_envelope(request, "examples/input.json") second = example.build_envelope(request, "examples/input.json") assert first == second assert json.dumps(first, sort_keys=True) == json.dumps(second, sort_keys=True)