File size: 3,637 Bytes
d2d1903 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | """
Tests for Stage 70 β bundle CLI.
python -m delivery.bundle create DIR [--out PATH]
python -m delivery.bundle verify ZIP
Recipient who got a bundle.zip in an email can verify in one shell
command. Exit-code contract:
0 β create succeeded OR verify passed (manifest intact)
1 β verify failed (sha256 mismatch on at least one file)
2 β bad args / file not found / not a zip
"""
import json
import zipfile
from delivery.bundle import _cli, bundle_pilot_output
def _populate(tmp_path):
src = tmp_path / "pilot_out"
src.mkdir()
(src / "report.html").write_text("<h1>R</h1>", encoding="utf-8")
(src / "queue.html").write_text("<h1>Q</h1>", encoding="utf-8")
return src
def _run(capsys, *args):
capsys.readouterr()
rc = _cli(list(args))
out, err = capsys.readouterr()
body = json.loads(out) if out.strip() else None
err_body = json.loads(err) if err.strip() else None
return rc, body, err_body
# --- create ----------------------------------------------------------
def test_create_emits_bundle_path_and_writes_zip(tmp_path, capsys):
src = _populate(tmp_path)
rc, body, _ = _run(capsys, "create", str(src))
assert rc == 0
assert "bundle_path" in body
zip_path = tmp_path / "pilot_out.zip"
assert zip_path.exists()
def test_create_with_explicit_out(tmp_path, capsys):
src = _populate(tmp_path)
out = tmp_path / "custom.zip"
rc, body, _ = _run(capsys, "create", str(src), "--out", str(out))
assert rc == 0
assert body["bundle_path"] == str(out.resolve())
assert out.exists()
def test_create_missing_dir_exits_2(tmp_path, capsys):
rc, _, err = _run(capsys, "create", str(tmp_path / "nope"))
assert rc == 2
assert "does not exist" in err["error"]
# --- verify ----------------------------------------------------------
def test_verify_intact_bundle_exits_0(tmp_path, capsys):
src = _populate(tmp_path)
zip_path = bundle_pilot_output(src)
rc, body, _ = _run(capsys, "verify", str(zip_path))
assert rc == 0
assert body["ok"] is True
assert body["n_mismatches"] == 0
def test_verify_tampered_bundle_exits_1(tmp_path, capsys):
"""The recipient's smoking gun β if anyone modified a file in the
bundle, verify returns exit 1 + ok=false + the mismatching paths
in the JSON. Cron-friendly: pipe to ``jq .ok`` and gate on it."""
src = _populate(tmp_path)
src_zip = bundle_pilot_output(src)
tampered = tmp_path / "tampered.zip"
with zipfile.ZipFile(src_zip, "r") as zin, \
zipfile.ZipFile(tampered, "w", zipfile.ZIP_DEFLATED) as zout:
for name in zin.namelist():
data = zin.read(name)
if name == "report.html":
data = b"<h1>EVIL</h1>"
zout.writestr(name, data)
rc, body, _ = _run(capsys, "verify", str(tampered))
assert rc == 1
assert body["ok"] is False
assert body["n_mismatches"] == 1
assert body["mismatches"][0]["path"] == "report.html"
def test_verify_missing_file_exits_2(tmp_path, capsys):
rc, _, err = _run(capsys, "verify", str(tmp_path / "no_such.zip"))
assert rc == 2
assert "error" in err
def test_verify_non_zip_exits_2(tmp_path, capsys):
"""A path that exists but isn't a zip β exit 2 with a clear
'not a valid zip' message. Operator pointing at the wrong file
sees the right error, not a Python traceback."""
bad = tmp_path / "not_a_zip.txt"
bad.write_text("plain text not a zip", encoding="utf-8")
rc, _, err = _run(capsys, "verify", str(bad))
assert rc == 2
assert "not a valid zip" in err["error"]
|