AICEPAICEP AICEP 0.1 — Public Draft (RFC)
AICEP is an experimental public draft, provided “as is” and without warranty. Use it at your own risk. We test it extensively, but testing is not a guarantee. Full disclaimer

← AICEP home · Runbook index

reference/tests/test_review_r1.py

Download test_review_r1.py

"""design review v2.3 review: each test pins a real bug. All six fixed in v2.4 (xfail markers removed). Originally xfail(strict=True) so the suite stayed green
until the source is fixed; when a fix lands the test XPASSes -> fails -> remove the marker."""
import json, os, shutil
import sys
import pytest, pyrage
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import aicep_sub
from aicep_common import Ledger, Reject, b64, canonical
from aicep_pub import publish
from test_delivery import w, send, OBJ  # noqa: F401 (fixture)
from test_tamper import env, pub, sub, resign  # noqa: F401 (fixture)

BUG = pytest.mark.xfail(strict=True, reason="design review v2.3 finding (open)")  # unused since v2.4


# fixed v2.4: V1 must: reorder-hold trusts an UNVERIFIED manifest seq
def test_unsigned_forged_high_seq_is_not_held(w):
    res = send(w); d = res["batch_dir"]
    files = {n: open(os.path.join(d, n), "rb").read() for n in os.listdir(d)}
    m = json.loads(files["manifest.json"]); m["seq"] = 999
    files["manifest.json"] = canonical(m)                     # signature now invalid
    w["bus"].place_raw("c1", "forged-x", files, folder="agent-a")
    shutil.rmtree(os.path.join(w["bus"].root, "c1", "pub", "agent-a", res["batch_id"]))
    assert w["recv"]().run_once()["forged-x"] == "dlq"         # actual: "held" (then gap_timeout email)


# fixed v2.4: V2 must: lease is not re-checked before commit
def test_expired_lease_holder_cannot_commit(w):
    send(w); r = w["recv"](consumer="r1")
    (d,) = w["bus"].fetch("c1", 10, 300, "r1", folders={"agent-a"})
    w["clk"].t += 301                                         # lease gone; r2 may now take it
    assert w["bus"].fetch("c1", 10, 300, "r2", folders={"agent-a"})                # r2 holds it too
    assert r.handle(d)[0] != "committed"


# fixed v2.4: V3 should: v2 sender can drop prev_batch_id and skip the chain check (downgrade)
def test_missing_prev_batch_id_after_chain_started_rejected(w):
    send(w); r = w["recv"](); r.run_once()
    b = publish(OBJ, [w["recip"]], w["sk"], "A", "c1", str(w["t"] / "f"), str(w["t"] / "fps.json"),
                now=w["clk"].t, seq=2, ttl=3600)
    w["bus"].publish(b["batch_dir"], {"compartment": "c1", "batch_id": b["batch_id"], "folder": "agent-a"})
    assert r.run_once()[b["batch_id"]] == "dlq"


# fixed v2.4: V4 should: size cap trusts declared size_padded, decrypts before checking real size
def test_size_cap_checks_bytes_on_disk_before_decrypt(env, monkeypatch):
    big = [{"name": "b", "type": "note", "classification": "internal", "body": "x" * 200_000}]
    d = publish(big, [env["recip"]], env["sk"], "swarmA", "c1", str(env["pub"]), env["pstate"], now=1_800_000_000, enforce_limits=False)["batch_dir"]
    resign(env, d, lambda m: [f.__setitem__("size_padded", 4096) for f in m["files"]])
    called = []
    real = pyrage.decrypt
    monkeypatch.setattr(aicep_sub.pyrage, "decrypt", lambda *a: called.append(1) or real(*a))
    with pytest.raises(Reject):
        sub(env, d)
    assert not called                                         # actual: decrypts 256 KiB first


# fixed v2.4: V5 should: non-int signed fields raise ValueError (not Reject) -> CLI traceback / receiver poison loop
def test_non_int_created_is_reject_not_crash(env):
    d = pub(env); resign(env, d, lambda m: m.__setitem__("created", "soon"))
    reg = json.loads(json.dumps(env["registry"]))
    for v in reg.values(): v["revoked_at"] = 1_800_000_000 + 1000
    with pytest.raises(Reject):
        sub(env, d, registry=reg)


# fixed v2.4: V6 should: ledger verify() can't detect tail truncation (no external anchor)
def test_ledger_tail_truncation_detected(w):
    send(w); send(w); w["recv"]().run_once()
    p = w["t"] / "r" / "ledger.jsonl"; head = Ledger(str(p)).head()
    lines = open(p).read().splitlines(); open(p, "w").write("\n".join(lines[:-2]) + "\n")
    L = Ledger(str(p))
    assert not L.verify() or L.head() == head                 # actual: verify() True, history silently shorter