"""TC-06: three-level never-raise. A raising collector (host / containers / deps) degrades ONE signal and the tick reaches the end collecting the rest; a raising send is swallowed; the daemon loop survives a raising tick. """ from watchdog.config import Config from watchdog.core import Watchdog class _BoomDocker: def inspect(self, name): raise RuntimeError("docker socket blew up") class _Notifier: def __init__(self): self.sent = [] def send(self, text): self.sent.append(text) return True class _BoomNotifier: def send(self, text): raise RuntimeError("telegram blew up") def _cfg(**kw): base = { "WATCHDOG_TG_BOT_TOKEN": "t", "WATCHDOG_TG_CHAT_ID": "c", "WATCHDOG_CONTAINERS": "orchestrator", } return Config.from_env({**base, **kw}) def _good_fetch_patch(dog, monkeypatch): from watchdog.collectors import orch as orch_mod env = {"schema_version": 1, "generated_at": "2026-06-10T00:00:00Z", "clk_tck": 100, "agents": [], "stages": [], "queue": {"depth": 0, "counts": {"failed": 0}}} monkeypatch.setattr( orch_mod, "fetch_metrics", lambda *a, **k: orch_mod.FetchResult(ok=True, envelope=env), ) def test_per_source_broken_container_degrades_one_signal(monkeypatch): notifier = _Notifier() dog = Watchdog(_cfg(), notifier=notifier, docker=_BoomDocker()) _good_fetch_patch(dog, monkeypatch) # Should not raise; tick completes and produces results for other sources. results = dog.tick() keys = [getattr(s, "key", None) for _, s in results] # orch_down evaluated (orch was up -> not active) and container evaluated. assert "orch_down" in keys assert ("container_down", "orchestrator") in keys def test_per_send_failure_is_swallowed(monkeypatch): # A raising notifier must not break the tick (per-send never-raise). cfg = _cfg(WATCHDOG_MEM_PCT="0") # mem >= 0 always -> force an alert send dog = Watchdog(cfg, notifier=_BoomNotifier(), docker=_BoomDocker()) _good_fetch_patch(dog, monkeypatch) monkeypatch.setattr( "watchdog.collectors.host.read_mem_used_pct", lambda *a, **k: 50.0 ) # Must not raise despite the notifier exploding on a triggered alert. dog.tick() def test_per_tick_loop_survives_raising_tick(monkeypatch): # The __main__ run loop must survive a tick that raises (outer never-raise). from watchdog import __main__ as entry cfg = _cfg(WATCHDOG_INTERVAL_S="0") class _BoomDog: def tick(self): raise RuntimeError("tick blew up") monkeypatch.setattr(entry, "Watchdog", lambda c: _BoomDog()) monkeypatch.setattr(entry.time, "sleep", lambda *_: None) # max_ticks bounds the loop; it must return cleanly, not propagate. entry.run(cfg=cfg, max_ticks=3)