"""v2.5 per-agent folders: pub/<folder>/<batch_id>, registry folder slugs, folder_mismatch."""
import json, os, shutil, sys
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
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__)))
from aicep_common import b64, key_id, valid_slug, check_slug, validate_registry_folders, active_folders, Ledger
from aicep_pub import Outbox, publish
from aicep_transport import LocalFolderAdapter
from test_delivery import w, send, OBJ, raw # noqa: F401
def add_sender(w, folder, sender_id="B"):
sk = Ed25519PrivateKey.generate()
w["reg"][key_id(raw(sk))] = {"sender_id": sender_id, "ed25519_pub": b64(raw(sk)), "state": "verified",
"verified_by": "human", "verified_at": w["clk"].t - 10,
"expires": w["clk"].t + 86400, "revoked_at": None, "folder": folder}
ob = Outbox(str(w["t"] / f"ob-{folder}.json"), sk, sender_id, str(w["t"] / f"l-{folder}.jsonl"),
now=w["clk"], folder=folder)
return sk, ob
def send_from(w, ob):
return ob.send(OBJ, [w["recip"]], "c1", w["bus"], str(w["t"] / f"work-{ob.folder}"), ttl=72000)
def test_happy_path_two_senders(w):
_, ob_b = add_sender(w, "agent-beta")
a = send(w); b = send_from(w, ob_b)
assert os.path.isdir(os.path.join(w["bus"].root, "c1", "pub", "agent-a", a["batch_id"]))
assert os.path.isdir(os.path.join(w["bus"].root, "c1", "pub", "agent-beta", b["batch_id"]))
out = w["recv"]().run_once()
assert out[a["batch_id"]][0] == "committed" and out[b["batch_id"]][0] == "committed"
a2 = send(w); b2 = send_from(w, ob_b)
out = w["recv"]().run_once()
assert out[a2["batch_id"]][0] == "committed" and out[b2["batch_id"]][0] == "committed"
def test_batch_in_other_senders_folder_rejected(w):
add_sender(w, "agent-beta")
a = send(w)
src = os.path.join(w["bus"].root, "c1", "pub", "agent-a", a["batch_id"])
dst = os.path.join(w["bus"].root, "c1", "pub", "agent-beta", a["batch_id"])
os.makedirs(os.path.dirname(dst), exist_ok=True); shutil.move(src, dst)
r = w["recv"]()
assert r.run_once()[a["batch_id"]] == "dlq"
reason = json.load(open(os.path.join(w["bus"].root, "c1", "dlq", a["batch_id"], "reason.json")))
assert reason["record"]["reason"] == "folder_mismatch"
assert any(n["event"] == "folder_mismatch" and n["email"] for n in r.notices)
def test_unregistered_folder_ignored_and_logged_once(w):
a = send(w)
src = os.path.join(w["bus"].root, "c1", "pub", "agent-a", a["batch_id"])
dst = os.path.join(w["bus"].root, "c1", "pub", "stranger-bot", a["batch_id"])
os.makedirs(os.path.dirname(dst)); shutil.move(src, dst)
r = w["recv"]()
assert r.run_once() == {} and r.run_once() == {}
assert os.path.isdir(dst) # untouched
L = Ledger(str(w["t"] / "r" / "ledger.jsonl"))
assert [e["folder"] for e in L.entries() if e["event"] == "unknown_folder_ignored"] == ["stranger-bot"]
def test_revoked_senders_folder_ignored(w):
_, ob_b = add_sender(w, "agent-beta")
b = send_from(w, ob_b)
for ent in w["reg"].values():
if ent["folder"] == "agent-beta":
ent["revoked_at"] = w["clk"].t - 1
assert "agent-beta" not in active_folders(w["reg"], w["clk"].t)
assert b["batch_id"] not in w["recv"]().run_once()
assert os.path.isdir(os.path.join(w["bus"].root, "c1", "pub", "agent-beta", b["batch_id"]))
def test_revoked_sender_batch_already_in_processing_not_redelivered(w):
_, ob_b = add_sender(w, "agent-beta")
b = send_from(w, ob_b)
assert w["bus"].fetch("c1", 10, 1, "r9", folders={"agent-beta"})
w["clk"].t += 5
assert w["bus"].fetch("c1", 10, 300, "r1", folders={"agent-a"}) == []
@pytest.mark.parametrize("slug", ["ab", "a" * 65, "Agent-alpha", "agent.alpha", "grok/bot", "..", "grok_bot",
"", None, "agent alpha"])
def test_bad_slug_refused(w, slug):
assert not valid_slug(slug)
with pytest.raises(ValueError):
check_slug(slug)
with pytest.raises(ValueError):
w["bus"].publish(str(w["t"]), {"compartment": "c1", "batch_id": "x", "folder": slug})
with pytest.raises(ValueError):
Outbox(str(w["t"] / "o.json"), Ed25519PrivateKey.generate(), "Z", str(w["t"] / "l.jsonl"), folder=slug)
def test_good_slugs():
for s in ("agent-alpha", "agent-beta", "abc", "a" * 64, "agent-007"):
assert valid_slug(s)
def test_registry_folder_must_be_unique_and_present(w):
add_sender(w, "agent-a") # duplicate of the fixture sender
with pytest.raises(ValueError, match="two keys"):
validate_registry_folders(w["reg"])
with pytest.raises(ValueError):
w["recv"]()
def test_registry_entry_without_folder_refused(w):
for ent in w["reg"].values():
ent.pop("folder")
with pytest.raises(ValueError):
w["recv"]()
def test_redrive_and_hold_return_to_same_folder(w):
res = send(w); d = os.path.join(w["bus"].root, "c1", "pub", "agent-a", res["batch_id"])
open(os.path.join(d, "meta.age"), "ab").write(b"x") # poison -> dlq
assert w["recv"]().run_once()[res["batch_id"]] == "dlq"
w["bus"].redrive("c1", res["batch_id"], operator="human-operator")
assert os.path.isdir(d)
from test_tamper import env, pub, NOW # noqa: E402,F401
from aicep_sub import ingest # noqa: E402
from aicep_common import Reject # noqa: E402
def test_ingest_folder_label_checked_against_verified_key(env):
d = pub(env)
with pytest.raises(Reject, match="folder mismatch"):
ingest(d, env["ident"], env["registry"], env["sstate"], "c1", now=NOW + 5, folder="other-agent")
assert ingest(d, env["ident"], env["registry"], env["sstate"], "c1", now=NOW + 5, folder="swarm-a")["status"] == "accepted"
# ---- v2.5.1 (design review v2.5 review X1-X3 + minor)
import subprocess # noqa: E402
from aicep_common import Ledger as _L # noqa: E402
def _P(w, *a):
return os.path.join(w["bus"].root, "c1", *a)
def test_x1_overflow_summary_row_written_once(w):
for i in range(100):
os.makedirs(_P(w, "pub", f"junk-{i}"))
os.makedirs(_P(w, "pub", "Bad.Name"))
r = w["recv"](); r.run_once(); r.run_once()
es = _L(str(w["t"] / "r" / "ledger.jsonl")).entries()
assert sum(e["event"] == "unknown_folder_ignored" for e in es) == 64
assert sum(e["event"] == "unknown_folder_overflow" for e in es) == 1
assert all(len(h) == 16 for h in r._r()["unknown_folders_logged"])
def test_x2_coalesced_alerts_one_per_folder_with_summary(w):
add_sender(w, "agent-beta"); add_sender(w, "other-bot", sender_id="C")
a = send(w)
for i in range(5):
shutil.copytree(_P(w, "pub", "agent-a", a["batch_id"]), _P(w, "pub", "agent-beta", f"x{i}"))
shutil.copytree(_P(w, "pub", "agent-a", a["batch_id"]), _P(w, "pub", "other-bot", f"y{i}"))
r = w["recv"](); r.run_once()
assert sum(1 for n in r.notices if n.get("email") and n["event"] == "folder_mismatch") == 2
s = [n for n in r.notices if n["event"] == "alerts_coalesced"]
assert s and not s[0]["email"] and sum(s[0]["counts"].values()) == 8
r2 = w["recv"](); send(w)
shutil.copytree(_P(w, "pub", "agent-a", sorted(os.listdir(_P(w, "pub", "agent-a")))[0]), _P(w, "pub", "agent-beta", "z"))
r2.run_once() # new run -> alert again
assert any(n.get("email") and n["event"] == "folder_mismatch" for n in r2.notices)
def test_x3_processing_resolves_folder_from_verified_skid(w):
a = send(w); r = w["recv"]()
(d,) = w["bus"].fetch("c1", 10, 1, "r1", folders={"agent-a"})
w["bus"].set_skid(d, next(iter(w["reg"]))) # verified once, then crash
for e in w["reg"].values():
e["folder"] = "agent-a2"
w["clk"].t += 5
assert r.run_once()[a["batch_id"]][0] == "committed"
def test_x3_receiver_redrive_uses_registry_folder(w):
res = send(w); d = _P(w, "pub", "agent-a", res["batch_id"])
open(os.path.join(d, "meta.age"), "ab").write(b"x")
r = w["recv"](); assert r.run_once()[res["batch_id"]] == "dlq"
for e in w["reg"].values():
e["folder"] = "agent-a2"
with pytest.raises(PermissionError):
r.redrive(res["batch_id"], " ")
r.redrive(res["batch_id"], "human-operator")
assert os.path.isdir(_P(w, "pub", "agent-a2", res["batch_id"]))
def test_symlinked_batch_file_rejected_by_receiver(w, tmp_path):
res = send(w); d = _P(w, "pub", "agent-a", res["batch_id"])
real = tmp_path / "outside.age"; shutil.move(os.path.join(d, "meta.age"), real)
os.symlink(real, os.path.join(d, "meta.age"))
assert w["recv"]().run_once()[res["batch_id"]] == "dlq"
def test_symlinked_batch_dir_skipped_by_fetch(w, tmp_path):
res = send(w); d = _P(w, "pub", "agent-a", res["batch_id"])
shutil.move(d, tmp_path / "real"); os.symlink(tmp_path / "real", d)
assert w["bus"].fetch("c1", 10, 300, "r1", folders={"agent-a"}) == []
def test_ingest_rejects_symlinked_dir_and_file(env, tmp_path):
d = pub(env)
link = tmp_path / "lnk"; os.symlink(d, link)
with pytest.raises(Reject, match="symlink"):
ingest(str(link), env["ident"], env["registry"], env["sstate"], "c1", now=NOW + 5, folder="swarm-a")
real = tmp_path / "m.age"; shutil.move(os.path.join(d, "meta.age"), real); os.symlink(real, os.path.join(d, "meta.age"))
with pytest.raises(Reject, match="symlink"):
ingest(d, env["ident"], env["registry"], env["sstate"], "c1", now=NOW + 5, folder="swarm-a")
def test_ingest_cli_requires_folder(tmp_path):
sub_py = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "aicep_sub.py")
r = subprocess.run([sys.executable, sub_py, str(tmp_path), "--compartment", "c1"], capture_output=True, text=True)
assert r.returncode != 0 and "--folder" in r.stderr