#!/usr/bin/env python3 """Tests for configs/felhom-internal-place (felhom.eu architecture/16 §5, §7 — sizes ruled 2026-10-16). The helper is run for real under bash; `lvs`, `lvcreate`, `blkid`, `mkfs.ext4` and `stat` are PATH stubs that read and write one JSON state file, so nothing on this machine is touched. Every refusal has a positive control: the same setup without the one condition goes through — so a test cannot pass because the helper refuses everything. Run: python3 configs/test_felhom_internal_place.py (also run by the Go suite, internal/internalplace) """ import json, os, pathlib, stat, subprocess, tempfile, unittest HELPER = pathlib.Path(__file__).resolve().parent / "felhom-internal-place" GIB = 1 << 30 STUB_LVS = r'''#!/usr/bin/env python3 import json, os, sys st = json.load(open(os.environ["IP_STATE"])) if st.get("lvs_fail_listing") and "lv_name,pool_lv,lv_size" in sys.argv: sys.exit(5) args = [a for a in sys.argv[1:] if not a.startswith("-") and a not in ("b",)] fields = sys.argv[sys.argv.index("-o") + 1].split(",") target = args[-1] listing = target == st["vg"] for lv in st["lvs"]: full = st["vg"] + "/" + lv["name"] if listing and st.get("hide_pool_in_listing") and lv.get("pool", "") == "": continue # a listing that silently lost the pool row (the pool's own size stays readable) if listing or target == full: row = {"lv_name": lv["name"], "pool_lv": lv.get("pool", ""), "lv_size": str(lv["size"])} print(" " + " ".join(row[f] for f in fields)) ''' STUB_LVCREATE = r'''#!/usr/bin/env python3 import json, os, sys p = os.environ["IP_STATE"]; st = json.load(open(p)) a = sys.argv[1:] size = int(a[a.index("-V") + 1].rstrip("G")) * (1 << 30) vg, pool = a[a.index("-T") + 1].split("/") st["lvs"].append({"name": a[a.index("-n") + 1], "pool": pool, "size": size, "active": True}) st.setdefault("calls", []).append("lvcreate " + " ".join(a)) if st.get("dirty_new"): st["fs"]["felhom-belso"] = {"TYPE": "", "RAW": "junk"} json.dump(st, open(p, "w")) ''' STUB_BLKID = r'''#!/usr/bin/env python3 # like blkid -p: exit 0 when something is found, 2 when nothing is; st["blkid_rc"] forces an error exit import json, os, sys st = json.load(open(os.environ["IP_STATE"])) if st.get("blkid_rc"): sys.exit(st["blkid_rc"]) fs = st["fs"].get(os.path.basename(sys.argv[-1]), {}) if "-s" in sys.argv: v = fs.get(sys.argv[sys.argv.index("-s") + 1], "") if v: print(v) sys.exit(0 if v else 2) vals = [fs.get(k, "") for k in ("TYPE", "LABEL", "UUID", "RAW") if fs.get(k)] if vals: print("\n".join(vals)) sys.exit(0 if vals else 2) ''' STUB_MKFS = r'''#!/usr/bin/env python3 import json, os, sys p = os.environ["IP_STATE"]; st = json.load(open(p)) dev = os.path.basename(sys.argv[-1]) st["fs"][dev] = {"TYPE": "ext4", "LABEL": sys.argv[sys.argv.index("-L") + 1], "UUID": "0b6e4c5a-1d2e-4f3a-9b8c-7d6e5f4a3b2c"} st.setdefault("calls", []).append("mkfs.ext4 " + " ".join(sys.argv[1:])) json.dump(st, open(p, "w")) ''' STUB_STAT = r'''#!/usr/bin/env python3 import json, os, sys st = json.load(open(os.environ["IP_STATE"])) name = os.path.basename(sys.argv[-1]) if any(lv["name"] == name and lv.get("active", True) for lv in st["lvs"]): print("block special file") else: sys.exit(1) ''' CFG = "dir: local\n\tpath /var/lib/vz\n\tcontent iso,backup\n\nlvmthin: local-lvm\n\tthinpool data\n\tvgname pve\n\tcontent rootdir,images\n" class Box: def __init__(self, pool_gib, lvs=(), fs=None, cfg=CFG, **extra): self.t = tempfile.TemporaryDirectory() r = pathlib.Path(self.t.name) self.bin = r / "bin"; self.bin.mkdir() for name, body in (("lvs", STUB_LVS), ("lvcreate", STUB_LVCREATE), ("blkid", STUB_BLKID), ("mkfs.ext4", STUB_MKFS), ("stat", STUB_STAT)): f = self.bin / name; f.write_text(body); f.chmod(0o755) self.cfg = r / "storage.cfg"; self.cfg.write_text(cfg) self.state = r / "state.json" st = {"vg": "pve", "lvs": [{"name": "data", "pool": "", "size": pool_gib * GIB}] + [dict(l) for l in lvs], "fs": fs or {}} st.update(extra) self.state.write_text(json.dumps(st)) def run(self, *args): env = {"PATH": f"{self.bin}:/usr/bin:/bin", "IP_STATE": str(self.state), "FELHOM_STORAGE_CFG": str(self.cfg)} p = subprocess.run(["bash", str(HELPER), *args], env=env, capture_output=True, text=True) return p.returncode, p.stdout.strip(), p.stderr.strip() def st(self): return json.loads(self.state.read_text()) def calls(self): return self.st().get("calls", []) def close(self): self.t.cleanup() def thin(name, gib, pool="data"): return {"name": name, "pool": pool, "size": gib * GIB} class Sizes(unittest.TestCase): """The operator's table (SPACE-CHECK §4, ruled 2026-10-16), as the helper computes it on a fresh box.""" def plan(self, pool, planned, lvs=()): b = Box(pool, lvs) try: rc, out, err = b.run("plan", str(planned)) self.assertEqual(rc, 0, err) self.assertEqual(b.calls(), [], "plan must change nothing") return dict(kv.split("=") for kv in out.split()[1:]) finally: b.close() def test_the_ruled_table(self): # disk → pool (GiB), data volume (planned = 32 + mp0), expected internal for pool, mp0, reserve, internal in ((140, 50, 16, 42), (349, 100, 34, 183), (800, 130, 48, 590), (1720, 130, 48, 1510)): got = self.plan(pool, 32 + mp0) self.assertEqual((int(got["reserve"]), int(got["internal"])), (reserve, internal), (pool, got)) def test_promised_volumes_count_but_vzdump_snapshots_and_other_pools_do_not(self): got = self.plan(349, 0, [thin("vm-9201-disk-0", 32), thin("vm-9201-disk-1", 250), thin("snap_vm-9201-disk-1_vzdump", 250), thin("vm-500-disk-0", 40, pool="other")]) self.assertEqual((int(got["promised"]), int(got["internal"])), (282, 33), got) class Create(unittest.TestCase): def test_a_fresh_box_gets_the_place(self): b = Box(349) try: rc, out, err = b.run("create", "132") self.assertEqual(rc, 0, err) self.assertRegex(out, r"^created uuid=[0-9a-f-]{36} gib=183$") self.assertEqual(len([c for c in b.calls() if c.startswith("lvcreate")]), 1) self.assertIn("-V 183G -T pve/data -n felhom-belso", b.calls()[0]) self.assertIn("-L felhom-belso", b.calls()[1]) finally: b.close() def test_too_little_space_makes_nothing(self): b = Box(53) # demo-hp's 128 GB disk try: rc, out, err = b.run("create", str(32 + 50)) self.assertEqual(rc, 0, err) self.assertTrue(out.startswith("skipped "), out) self.assertEqual(b.calls(), []) finally: b.close() def test_an_existing_place_is_found_never_formatted(self): fs = {"felhom-belso": {"TYPE": "ext4", "LABEL": "felhom-belso", "UUID": "11111111-2222-3333-4444-555555555555"}} b = Box(349, [thin("felhom-belso", 183)], fs) try: rc, out, err = b.run("create", "0") self.assertEqual(rc, 0, err) self.assertEqual(out, "existing uuid=11111111-2222-3333-4444-555555555555 gib=183") self.assertEqual(b.calls(), [], "an existing place must never be created or formatted again") finally: b.close() def test_a_foreign_filesystem_on_the_name_is_refused(self): # positive control: test_an_existing_place_is_found_never_formatted (same LV, Felhom's label) goes through for fs in ({"TYPE": "xfs", "LABEL": "felhom-belso", "UUID": "x"}, {"TYPE": "ext4", "LABEL": "films", "UUID": "y"}): b = Box(349, [thin("felhom-belso", 183)], {"felhom-belso": fs}) try: rc, out, err = b.run("create", "0") self.assertEqual(rc, 1, (fs, out)) self.assertIn("REFUSED", err) self.assertEqual(b.calls(), [], fs) finally: b.close() def test_a_crash_between_lvcreate_and_mkfs_is_finished(self): b = Box(349, [thin("felhom-belso", 183)], {}) try: rc, out, err = b.run("create", "0") self.assertEqual(rc, 0, err) self.assertRegex(out, r"^formatted uuid=") self.assertEqual([c.split()[0] for c in b.calls()], ["mkfs.ext4"]) finally: b.close() def test_an_unnamed_signature_is_refused(self): b = Box(349, [thin("felhom-belso", 183)], {"felhom-belso": {"RAW": "something"}}) try: rc, out, err = b.run("create", "0") self.assertEqual(rc, 1, out) self.assertEqual(b.calls(), []) finally: b.close() def test_a_new_volume_that_is_not_empty_is_not_formatted(self): b = Box(349, dirty_new=True) try: rc, out, err = b.run("create", "132") self.assertEqual(rc, 1, out) self.assertNotIn("mkfs", " ".join(b.calls())) finally: b.close() def test_bad_arguments_and_no_local_lvm_are_refused(self): b = Box(349, cfg="dir: local\n\tpath /var/lib/vz\n") try: self.assertEqual(b.run("create", "0")[0], 1) self.assertEqual(b.calls(), []) finally: b.close() b = Box(349) try: for args in (("create", "-1"), ("create", "1x"), ("wipe", "0"), ("create",)): self.assertEqual(b.run(*args)[0], 1, args) self.assertEqual(b.calls(), []) finally: b.close() class FailsClosed(unittest.TestCase): """A read that fails must never read as „empty" or „nothing promised". Positive control: Create (same setups).""" def test_a_failed_volume_listing_creates_nothing(self): b = Box(349, lvs_fail_listing=True) try: rc, out, err = b.run("create", "132") self.assertEqual(rc, 1, out) self.assertEqual(b.calls(), []) finally: b.close() def test_a_listing_without_the_pool_creates_nothing(self): b = Box(349, [thin("vm-9201-disk-1", 250)], hide_pool_in_listing=True) try: self.assertEqual(b.run("create", "132")[0], 1) self.assertEqual(b.calls(), []) finally: b.close() def test_a_blkid_error_never_reads_as_empty(self): for setup in ({"lvs": [thin("felhom-belso", 183)], "fs": {}}, {}): b = Box(349, **({"lvs": setup["lvs"], "fs": setup["fs"]} if setup else {}), blkid_rc=4) try: rc, out, err = b.run("create", "0" if setup else "132") self.assertEqual(rc, 1, (setup, out)) self.assertNotIn("mkfs", " ".join(b.calls()), setup) finally: b.close() if __name__ == "__main__": unittest.main()