72681db46b
Each tick lists skipped items with terminal and reason (concurrency n/m with the holding runs, serialize group holder, busy_process not started by agentd, relay down, no route for assignee, start failed) in heartbeat.json "skipped". A reason is logged (agentd.log / cron.log) only when it is new for that item; the last set is kept in the state file as "skips".
72 lines
2.8 KiB
Python
72 lines
2.8 KiB
Python
import json
|
|
import unittest
|
|
|
|
from tests.fakes import Harness
|
|
|
|
|
|
class FakeProbe:
|
|
def __init__(self, counts):
|
|
self.counts = counts
|
|
|
|
def process_count(self, name):
|
|
return self.counts.get(name, 0)
|
|
|
|
|
|
class SkipReasonTest(unittest.TestCase):
|
|
"""Why a Todo item was not dispatched: heartbeat "skipped", logged once per new reason."""
|
|
|
|
def setUp(self):
|
|
self.h = Harness()
|
|
self.p = self.h.plane
|
|
self.lines = []
|
|
self.h.d.log = lambda msg: self.lines.append(msg)
|
|
|
|
def tearDown(self):
|
|
self.h.close()
|
|
|
|
def skipped(self):
|
|
hb = json.loads((self.h.d.store.dir / "heartbeat.json").read_text())
|
|
return {s["item"]: (s["terminal"], s["reason"]) for s in hb["skipped"]}
|
|
|
|
def not_dispatched(self):
|
|
return [l for l in self.lines if "not dispatched" in l]
|
|
|
|
def test_reasons_in_heartbeat_and_logged_once(self):
|
|
self.p.add_issue(1) # starts on win10
|
|
self.p.add_issue(2) # win10 full
|
|
self.p.add_issue(3, assignee="kappiee") # no terminal for kappiee
|
|
self.p.add_issue(4, assignee="oregon", labels=["needs-game"]) # starts on oregon, holds the game group
|
|
self.p.add_issue(5, assignee="oregon", labels=["needs-game"]) # group held
|
|
self.h.tick()
|
|
self.assertEqual(self.skipped(), {
|
|
"GW-2": ("win10", "concurrency 1/1 (GW-1)"),
|
|
"GW-3": (None, "no route for assignee kappiee"),
|
|
"GW-5": ("oregon", "serialize_group game held by GW-4"),
|
|
})
|
|
self.assertEqual(len(self.not_dispatched()), 3)
|
|
self.h.tick() # same reasons: heartbeat still has them, the log does not repeat them
|
|
self.assertEqual(len(self.skipped()), 3)
|
|
self.assertEqual(len(self.not_dispatched()), 3)
|
|
|
|
def test_busy_process_and_relay_down(self):
|
|
self.h.cfg.terminals["win10"]["busy_processes"] = ["codex"]
|
|
probe = FakeProbe({"codex": 1})
|
|
self.h.d.transport_factory = lambda term: probe
|
|
self.p.add_issue(1)
|
|
self.h.tick()
|
|
self.assertEqual(self.skipped(), {"GW-1": ("win10", "busy_process codex (1 running, not started by agentd)")})
|
|
probe.counts = {}
|
|
self.h.tick() # leftover codex.exe gone: dispatched, nothing skipped
|
|
self.assertEqual(self.skipped(), {})
|
|
self.assertEqual(self.p.state_of("i1"), "In Progress")
|
|
self.p.add_issue(2, assignee="oregon")
|
|
self.h.cfg.terminals["oregon"]["busy_processes"] = ["claude"]
|
|
probe.counts = {"claude": None} # probe fails: relay down
|
|
probe.process_count = lambda name: None
|
|
self.h.tick()
|
|
self.assertEqual(self.skipped()["GW-2"], ("oregon", "relay down"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|