"""Tests for the m2pack wrapper subcommands. All tests use a stub binary — a small Python script that echoes a canned JSON envelope based on the subcommand name — pointed at via the ``M2PACK_BINARY`` env var. The real m2pack-secure binary is never invoked. """ from __future__ import annotations import json import stat import sys from pathlib import Path import pytest from metin_release.cli import main as cli_main STUB_TEMPLATE = r"""#!{python} import json import sys argv = sys.argv[1:] sub = argv[0] if argv else "unknown" MODE = {mode!r} if MODE == "fail": sys.stderr.write("boom\n") sys.exit(2) if MODE == "nonjson": sys.stdout.write("not json at all\n") sys.exit(0) if MODE == "notobject": sys.stdout.write(json.dumps([1, 2, 3]) + "\n") sys.exit(0) # mode == "ok": echo a canned envelope per subcommand if sub == "build": env = {{"ok": True, "command": "build", "stats": {{"files": 3, "bytes": 12345}}}} elif sub == "verify": env = {{"ok": True, "command": "verify", "signature": "valid"}} elif sub == "diff": env = {{"ok": True, "command": "diff", "added": ["a"], "removed": [], "changed": ["b", "c"], "unchanged": 7}} elif sub == "export-runtime-key": env = {{"ok": True, "command": "export-runtime-key", "key_id": 1, "format": "json"}} else: env = {{"ok": True, "command": sub}} # Record argv so the test can assert translation import os log = os.environ.get("M2PACK_STUB_LOG") if log: with open(log, "a") as fh: fh.write(json.dumps(argv) + "\n") sys.stdout.write(json.dumps(env) + "\n") sys.exit(0) """ def _install_stub(tmp_path: Path, monkeypatch, mode: str = "ok") -> tuple[Path, Path]: stub = tmp_path / "m2pack_stub.py" stub.write_text(STUB_TEMPLATE.format(python=sys.executable, mode=mode)) stub.chmod(stub.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) log = tmp_path / "stub.log" monkeypatch.setenv("M2PACK_BINARY", str(stub)) monkeypatch.setenv("M2PACK_STUB_LOG", str(log)) return stub, log def _run_cli(argv: list[str], capsys) -> dict: rc = cli_main(argv) out = capsys.readouterr().out envelope = json.loads(out) return {"rc": rc, "env": envelope} def _read_stub_call(log: Path) -> list[str]: lines = [ln for ln in log.read_text().splitlines() if ln.strip()] assert lines, "stub was never invoked" return json.loads(lines[-1]) # --------------------------------------------------------------------------- # Success paths # --------------------------------------------------------------------------- def test_build_success(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) (tmp_path / "in").mkdir() key = tmp_path / "k.hex" key.write_text("ff") sk = tmp_path / "sk.hex" sk.write_text("aa") out = tmp_path / "out.m2p" r = _run_cli( [ "--json", "m2pack", "build", "--input", str(tmp_path / "in"), "--output", str(out), "--key", str(key), "--sign-secret-key", str(sk), "--key-id", "7", ], capsys, ) assert r["rc"] == 0 assert r["env"]["ok"] is True assert r["env"]["command"] == "m2pack build" assert r["env"]["status"] == "built" assert r["env"]["artifacts"]["archive_path"] == str(out) assert r["env"]["m2pack"]["stats"]["files"] == 3 call = _read_stub_call(log) assert call[0] == "build" assert "--input" in call and str(tmp_path / "in") in call assert "--output" in call and str(out) in call assert "--key" in call and str(key) in call assert "--sign-secret-key" in call and str(sk) in call assert "--key-id" in call and "7" in call assert "--json" in call def test_build_omits_optional_key_id(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) (tmp_path / "in").mkdir() (tmp_path / "k").write_text("a") (tmp_path / "sk").write_text("b") _run_cli( [ "--json", "m2pack", "build", "--input", str(tmp_path / "in"), "--output", str(tmp_path / "o.m2p"), "--key", str(tmp_path / "k"), "--sign-secret-key", str(tmp_path / "sk"), ], capsys, ) call = _read_stub_call(log) assert "--key-id" not in call def test_verify_success_without_keys(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) archive = tmp_path / "a.m2p" archive.write_bytes(b"x") r = _run_cli( ["--json", "m2pack", "verify", "--archive", str(archive)], capsys ) assert r["rc"] == 0 assert r["env"]["status"] == "verified" assert r["env"]["m2pack"]["signature"] == "valid" call = _read_stub_call(log) assert call[0] == "verify" assert "--public-key" not in call assert "--key" not in call def test_verify_with_public_and_content_keys(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) archive = tmp_path / "a.m2p" archive.write_bytes(b"x") pk = tmp_path / "pub" pk.write_text("ff") ck = tmp_path / "ck" ck.write_text("aa") _run_cli( [ "--json", "m2pack", "verify", "--archive", str(archive), "--public-key", str(pk), "--key", str(ck), ], capsys, ) call = _read_stub_call(log) assert "--public-key" in call and str(pk) in call assert "--key" in call and str(ck) in call def test_diff_success_promotes_counts(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) left = tmp_path / "L" left.mkdir() right = tmp_path / "R.m2p" right.write_bytes(b"x") r = _run_cli( [ "--json", "m2pack", "diff", "--left", str(left), "--right", str(right), ], capsys, ) assert r["rc"] == 0 assert r["env"]["status"] == "diffed" stats = r["env"]["stats"] assert stats["added_count"] == 1 assert stats["removed_count"] == 0 assert stats["changed_count"] == 2 assert stats["unchanged_count"] == 7 call = _read_stub_call(log) assert call[0] == "diff" def test_export_runtime_key_success(tmp_path, monkeypatch, capsys): _, log = _install_stub(tmp_path, monkeypatch) key = tmp_path / "ck" key.write_text("aa") pk = tmp_path / "pk" pk.write_text("ff") out = tmp_path / "runtime.json" r = _run_cli( [ "--json", "m2pack", "export-runtime-key", "--key", str(key), "--public-key", str(pk), "--output", str(out), "--key-id", "3", "--format", "blob", ], capsys, ) assert r["rc"] == 0 assert r["env"]["status"] == "exported" assert r["env"]["artifacts"]["runtime_key_path"] == str(out) call = _read_stub_call(log) assert call[0] == "export-runtime-key" assert "--format" in call and "blob" in call assert "--key-id" in call and "3" in call def test_export_runtime_key_rejects_bad_format(tmp_path, monkeypatch, capsys): _install_stub(tmp_path, monkeypatch) with pytest.raises(SystemExit): cli_main( [ "--json", "m2pack", "export-runtime-key", "--key", str(tmp_path / "k"), "--public-key", str(tmp_path / "p"), "--output", str(tmp_path / "o"), "--format", "yaml", ] ) # --------------------------------------------------------------------------- # Error paths # --------------------------------------------------------------------------- def test_nonzero_exit_maps_to_subprocess_failed(tmp_path, monkeypatch, capsys): _install_stub(tmp_path, monkeypatch, mode="fail") (tmp_path / "in").mkdir() (tmp_path / "k").write_text("a") (tmp_path / "sk").write_text("b") rc = cli_main( [ "--json", "m2pack", "build", "--input", str(tmp_path / "in"), "--output", str(tmp_path / "o.m2p"), "--key", str(tmp_path / "k"), "--sign-secret-key", str(tmp_path / "sk"), ], ) assert rc == 1 env = json.loads(capsys.readouterr().out) assert env["ok"] is False assert env["error"]["code"] == "m2pack_failed" def test_nonjson_output_maps_to_invalid_json(tmp_path, monkeypatch, capsys): _install_stub(tmp_path, monkeypatch, mode="nonjson") archive = tmp_path / "a.m2p" archive.write_bytes(b"x") rc = cli_main(["--json", "m2pack", "verify", "--archive", str(archive)]) assert rc == 1 env = json.loads(capsys.readouterr().out) assert env["ok"] is False assert env["error"]["code"] == "m2pack_invalid_json" def test_json_array_output_maps_to_invalid_json(tmp_path, monkeypatch, capsys): _install_stub(tmp_path, monkeypatch, mode="notobject") archive = tmp_path / "a.m2p" archive.write_bytes(b"x") rc = cli_main(["--json", "m2pack", "verify", "--archive", str(archive)]) assert rc == 1 env = json.loads(capsys.readouterr().out) assert env["error"]["code"] == "m2pack_invalid_json" def test_missing_binary_raises_validation_error(tmp_path, monkeypatch, capsys): monkeypatch.delenv("M2PACK_BINARY", raising=False) monkeypatch.setenv("PATH", "/nonexistent") archive = tmp_path / "a.m2p" archive.write_bytes(b"x") rc = cli_main(["--json", "m2pack", "verify", "--archive", str(archive)]) assert rc == 1 env = json.loads(capsys.readouterr().out) assert env["ok"] is False assert env["error"]["code"] == "m2pack_not_found"