"""Tests for :mod:`metin_release_mcp.cli_runner`.""" from __future__ import annotations import json import pytest from metin_release_mcp import cli_runner from metin_release_mcp.errors import CliNotFoundError, CliUnparseableOutputError from .conftest import make_runner def test_run_cli_success_parses_envelope(): runner = make_runner({"ok": True, "command": "release inspect", "status": "inspected", "stats": {"file_count": 3}}) result = cli_runner.run_cli( ["release", "inspect", "--json", "--source", "/tmp/x"], binary="/fake/metin-release", runner=runner, ) assert result.ok is True assert result.envelope["status"] == "inspected" assert result.envelope["stats"]["file_count"] == 3 assert result.returncode == 0 assert runner.calls[0][0] == "/fake/metin-release" assert "--json" in runner.calls[0] def test_run_cli_error_envelope_passed_through(): err = { "ok": False, "command": "release sign", "status": "failed", "error": {"code": "key_permission", "message": "key must be mode 600"}, } runner = make_runner(err, stderr="error: key_permission\n", returncode=3) result = cli_runner.run_cli(["release", "sign"], binary="/fake/mr", runner=runner) assert result.ok is False assert result.returncode == 3 assert result.envelope["error"]["code"] == "key_permission" assert "key_permission" in result.stderr def test_run_cli_unparseable_output_raises(): runner = make_runner(stdout="not-json-at-all", returncode=0) with pytest.raises(CliUnparseableOutputError): cli_runner.run_cli(["release", "inspect"], binary="/fake/mr", runner=runner) def test_run_cli_empty_stdout_raises(): runner = make_runner(stdout=" \n", stderr="boom", returncode=2) with pytest.raises(CliUnparseableOutputError): cli_runner.run_cli(["release", "inspect"], binary="/fake/mr", runner=runner) def test_run_cli_non_object_json_raises(): runner = make_runner(stdout=json.dumps([1, 2, 3])) with pytest.raises(CliUnparseableOutputError): cli_runner.run_cli(["release", "inspect"], binary="/fake/mr", runner=runner) def test_run_cli_file_not_found_raises(): def boom(*args, **kwargs): raise FileNotFoundError("no such file") with pytest.raises(CliNotFoundError): cli_runner.run_cli(["release", "inspect"], binary="/does/not/exist", runner=boom) def test_resolve_binary_env_override(monkeypatch): monkeypatch.setenv("METIN_RELEASE_BINARY", "/custom/path/metin-release") assert cli_runner.resolve_binary() == "/custom/path/metin-release" def test_resolve_binary_which_fallback(monkeypatch): monkeypatch.delenv("METIN_RELEASE_BINARY", raising=False) monkeypatch.setattr(cli_runner.shutil, "which", lambda name: "/usr/bin/metin-release") assert cli_runner.resolve_binary() == "/usr/bin/metin-release" def test_resolve_binary_missing_raises(monkeypatch): monkeypatch.delenv("METIN_RELEASE_BINARY", raising=False) monkeypatch.setattr(cli_runner.shutil, "which", lambda name: None) with pytest.raises(CliNotFoundError): cli_runner.resolve_binary()