Guard seat test scripts behind __main__; clean _t_* cron output residue

Both harnesses executed top-to-bottom on import, so anything collecting
them (pytest, a glob import) ran live-store side effects. main() guards
plus finally-block cleanup of the _t_* cron output dirs the fabricated
jobs leave behind.
This commit is contained in:
vh
2026-09-19 07:04:15 -07:00
parent 0dc8e9096e
commit 0fe4da64c4
2 changed files with 160 additions and 127 deletions
+71 -61
View File
@@ -9,79 +9,89 @@ Controls per forseti (2740):
- crash case: persist detection, simulate death before 'read', reload state
as after restart, rerun with SAME id set -> suppressed (the finding)
Each condition repeated 3x.
Guarded behind __main__: importing this module (e.g. by pytest collection)
must not monkeypatch the live cron.monitor module or execute it.
"""
import sys, copy
sys.path.insert(0, "/home/lkraven/.hermes/hermes-agent")
from cron import monitor
STATE = {} # in-memory stand-in for jobs.json monitor_state
def fake_persist(job_id, new_hash, output):
STATE[job_id] = {"last_output_hash": new_hash, "snapshot": output}
def main():
sys.path.insert(0, "/home/lkraven/.hermes/hermes-agent")
from cron import monitor
monitor._persist_monitor_state = fake_persist
monitor._read_last_output = lambda job_id: STATE.get(job_id, {}).get("snapshot", "")
monitor._write_last_output = lambda job_id, output: STATE[job_id].__setitem__("snapshot", output)
STATE = {} # in-memory stand-in for jobs.json monitor_state
OUTPUT = [""] # current observed monitor output, set per tick
def fake_persist(job_id, new_hash, output):
STATE[job_id] = {"last_output_hash": new_hash, "snapshot": output}
def fake_source(job):
return True, OUTPUT[0]
monitor._persist_monitor_state = fake_persist
monitor._read_last_output = lambda job_id: STATE.get(job_id, {}).get("snapshot", "")
monitor._write_last_output = lambda job_id, output: STATE[job_id].__setitem__("snapshot", output)
monitor._run_monitor_source = fake_source
OUTPUT = [""] # current observed monitor output, set per tick
def tick(ids):
"""One monitor tick with the given unread id set. Returns (ok, changed)."""
OUTPUT[0] = "\n".join(f"[{i}]" for i in ids) + "\n"
job = {"id": "disposable-test-job", "monitor_script": "stub"}
if "disposable-test-job" in STATE: # simulate fresh load from store
job["monitor_state"] = copy.deepcopy(STATE["disposable-test-job"])
out = monitor.check_monitor(job)
return out.ok, getattr(out, "changed", None)
def fake_source(job):
return True, OUTPUT[0]
results = []
for rep in range(1, 4):
# --- consumption control: a SUCCESSFUL run consumes -> output genuinely changes.
# (Was: same ids after success, which only proved unchanged-input suppression.
# Fix: after the run consumes, the mailbox is empty, so the next observation
# is the idle sentinel; that transition admits once, stable idle suppresses.)
STATE.clear()
r1 = tick(["100"]) # first observation -> wake
r2 = tick([]) # agent consumed -> monitor now says idle
r3 = tick([]) # still idle -> suppressed
results.append((f"rep{rep} control-consume: first={r1} after-consume={r2} stable-idle={r3}",
r1 == (True, True) and r2 == (True, True) and r3 == (True, False)))
monitor._run_monitor_source = fake_source
# --- suppression control (renamed honestly): unchanged output suppresses ---
STATE.clear()
r1 = tick(["100"])
r2 = tick(["100"]) # no consumption, same set next tick
results.append((f"rep{rep} control-unchanged-suppresses: first={r1} unchanged={r2}",
r1 == (True, True) and r2 == (True, False)))
def tick(ids):
"""One monitor tick with the given unread id set. Returns (ok, changed)."""
OUTPUT[0] = "\n".join(f"[{i}]" for i in ids) + "\n"
job = {"id": "disposable-test-job", "monitor_script": "stub"}
if "disposable-test-job" in STATE: # simulate fresh load from store
job["monitor_state"] = copy.deepcopy(STATE["disposable-test-job"])
out = monitor.check_monitor(job)
return out.ok, getattr(out, "changed", None)
# --- positive control: changed ID set admits another run ---
STATE.clear()
r1 = tick(["100"])
r2 = tick(["100", "101"]) # new mail -> changed -> wake
results.append((f"rep{rep} control-change: first={r1} changed={r2}",
r1 == (True, True) and r2 == (True, True)))
results = []
for rep in range(1, 4):
# --- consumption control: a SUCCESSFUL run consumes -> output genuinely changes.
# (Was: same ids after success, which only proved unchanged-input suppression.
# Fix: after the run consumes, the mailbox is empty, so the next observation
# is the idle sentinel; that transition admits once, stable idle suppresses.)
STATE.clear()
r1 = tick(["100"]) # first observation -> wake
r2 = tick([]) # agent consumed -> monitor now says idle
r3 = tick([]) # still idle -> suppressed
results.append((f"rep{rep} control-consume: first={r1} after-consume={r2} stable-idle={r3}",
r1 == (True, True) and r2 == (True, True) and r3 == (True, False)))
# --- crash case: detection persisted, agent died BEFORE consuming mail ---
STATE.clear()
r1 = tick(["200", "201"]) # detection: hash persisted pre-agent
# (agent session would run `postbox read` here; simulate it dying first)
r2 = tick(["200", "201"]) # next tick, same unread set, post-restart reload
r3 = tick(["200", "201"]) # and again: unbounded silence
results.append((f"rep{rep} crash-before-read: detect={r1} retry1={r2} retry2={r3}",
r1 == (True, True) and r2 == (True, False) and r3 == (True, False)))
# --- suppression control (renamed honestly): unchanged output suppresses ---
STATE.clear()
r1 = tick(["100"])
r2 = tick(["100"]) # no consumption, same set next tick
results.append((f"rep{rep} control-unchanged-suppresses: first={r1} unchanged={r2}",
r1 == (True, True) and r2 == (True, False)))
# --- recovery path: stranded mail + a NEW message eventually wakes ---
r4 = tick(["200", "201", "202"]) # a genuinely new message re-wakes,
results.append((f"rep{rep} recovery-on-new-mail: {r4}", r4 == (True, True)))
# --- positive control: changed ID set admits another run ---
STATE.clear()
r1 = tick(["100"])
r2 = tick(["100", "101"]) # new mail -> changed -> wake
results.append((f"rep{rep} control-change: first={r1} changed={r2}",
r1 == (True, True) and r2 == (True, True)))
ok = True
for line, passed in results:
print(("PASS " if passed else "FAIL ") + line)
ok = ok and passed
print("ALL PASS" if ok else "SOME FAILED")
sys.exit(0 if ok else 1)
# --- crash case: detection persisted, agent died BEFORE consuming mail ---
STATE.clear()
r1 = tick(["200", "201"]) # detection: hash persisted pre-agent
# (agent session would run `postbox read` here; simulate it dying first)
r2 = tick(["200", "201"]) # next tick, same unread set, post-restart reload
r3 = tick(["200", "201"]) # and again: unbounded silence
results.append((f"rep{rep} crash-before-read: detect={r1} retry1={r2} retry2={r3}",
r1 == (True, True) and r2 == (True, False) and r3 == (True, False)))
# --- recovery path: stranded mail + a NEW message eventually wakes ---
r4 = tick(["200", "201", "202"]) # a genuinely new message re-wakes,
results.append((f"rep{rep} recovery-on-new-mail: {r4}", r4 == (True, True)))
ok = True
for line, passed in results:
print(("PASS " if passed else "FAIL ") + line)
ok = ok and passed
print("ALL PASS" if ok else "SOME FAILED")
sys.exit(0 if ok else 1)
if __name__ == "__main__":
main()