""" smoke_documents.py - the members document gate, per unit. Builds a throwaway /docs tree and manifest, then checks who can SEE and who can LIST each document. Runs in-process, stdlib only. python3 tests/smoke_documents.py The rules under test are the ones that fail silently: a members document must be unservable AND unlisted to the wrong viewer, unlisted must stay servable but off the index, and listing must never be able to show something serving would refuse. """ import json, os, sys, tempfile TMP = tempfile.mkdtemp() DOCS = os.path.join(TMP, "docs"); os.makedirs(DOCS) os.environ["DOCS_DIR"] = DOCS os.environ["STORE_DB"] = os.path.join(TMP, "smoke.db") sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "app")) FILES = ["open.pdf", "hidden.pdf", "packonly.pdf", "trooponly.pdf", "bothunits.pdf"] for f in FILES: open(os.path.join(DOCS, f), "w").write("x") json.dump({ "categories": [{"id": "forms", "title": "Forms", "blurb": ""}], "documents": [ {"slug": "open", "file": "open.pdf", "title": "Open", "category": "forms", "visibility": "public", "unit": "both"}, {"slug": "hidden", "file": "hidden.pdf", "title": "Hidden", "category": "forms", "visibility": "unlisted", "unit": "both"}, {"slug": "packonly", "file": "packonly.pdf", "title": "Pack only", "category": "forms", "visibility": "members", "unit": "pack"}, {"slug": "trooponly", "file": "trooponly.pdf", "title": "Troop only", "category": "forms", "visibility": "members", "unit": "troop"}, {"slug": "bothunits", "file": "bothunits.pdf", "title": "Both", "category": "forms", "visibility": "members", "unit": "both"}, ]}, open(os.path.join(DOCS, "manifest.json"), "w")) import documents as D PASS = FAIL = 0 def check(label, cond): global PASS, FAIL if cond: PASS += 1; print(" ok %s" % label) else: FAIL += 1; print(" FAIL %s" % label) def served(units): return {s for s in ("open", "hidden", "packonly", "trooponly", "bothunits") if D.find(s, units) is not None} def listed(units): return {d["slug"] for _, rows in D.listing(units) for d in rows} anon = frozenset() pack = D.units_for({"global_role": None, "memberships": [{"unit_type": "pack", "role": "member"}]}) troop = D.units_for({"global_role": None, "memberships": [{"unit_type": "troop", "role": "leader"}]}) both = D.units_for({"global_role": None, "memberships": [{"unit_type": "pack", "role": "member"}, {"unit_type": "troop", "role": "leader"}]}) admin = D.units_for({"global_role": "admin", "memberships": []}) crew = D.units_for({"global_role": None, "memberships": [{"unit_type": "crew", "role": "leader"}]}) off = D.units_for({"global_role": "owner", "memberships": [], "disabled_at": "2026-01-01T00:00:00+00:00"}) print("units_for") check("anonymous gets nothing", D.units_for(None) == frozenset()) check("pack member -> {pack}", pack == frozenset({"pack"})) check("admin -> every unit type", admin == frozenset(D.DOC_UNITS)) check("crew maps to nothing (manifest has no crew)", crew == frozenset()) check("disabled owner gets nothing", off == frozenset()) print("\nserving") check("anon: public and unlisted only", served(anon) == {"open", "hidden"}) check("pack member: + pack and both", served(pack) == {"open", "hidden", "packonly", "bothunits"}) check("troop leader: + troop and both", served(troop) == {"open", "hidden", "trooponly", "bothunits"}) check("in both units: everything", served(both) == set(FILES and {"open", "hidden", "packonly", "trooponly", "bothunits"})) check("admin: everything", served(admin) == {"open", "hidden", "packonly", "trooponly", "bothunits"}) check("pack member cannot reach troop doc", D.find("trooponly", pack) is None) check("crew leader reaches no members doc", served(crew) == {"open", "hidden"}) print("\nlisting") check("anon index is public only", listed(anon) == {"open"}) check("unlisted never listed, for anyone", "hidden" not in listed(admin)) check("pack member index", listed(pack) == {"open", "packonly", "bothunits"}) check("troop leader index", listed(troop) == {"open", "trooponly", "bothunits"}) check("admin index", listed(admin) == {"open", "packonly", "trooponly", "bothunits"}) print("\nlisting can never exceed serving") for name, u in (("anon", anon), ("pack", pack), ("troop", troop), ("admin", admin), ("crew", crew)): check("%s: listed is a subset of served" % name, listed(u) <= served(u)) print("\nmissing file on disk") os.remove(os.path.join(DOCS, "packonly.pdf")) D._cache["key"] = None check("a members doc with no file is not served", D.find("packonly", pack) is None) check("and not listed", "packonly" not in listed(pack)) print("\n%d passed, %d failed" % (PASS, FAIL)) sys.exit(1 if FAIL else 0)