"""TC-05: orchestrator-down detection. A ``/metrics`` timeout / connection-refused / 5xx / unreadable body -> the ``orchestrator_down`` signal -> ALERT "орк не отвечает" once the debounce threshold of consecutive failures is reached (FR-3). """ from watchdog.collectors import orch as orch_mod from watchdog.config import Config from watchdog.signals import orch_down_signal from .conftest import http_error, make_opener def _cfg(**kw): return Config.from_env({**{"WATCHDOG_ORCH_DOWN_TICKS": "3"}, **kw}) def test_fetch_timeout_is_not_ok(): opener = make_opener(exc=TimeoutError("timed out")) res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is False assert res.envelope is None assert res.error def test_fetch_connection_refused_is_not_ok(): opener = make_opener(exc=ConnectionRefusedError("refused")) res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is False def test_fetch_5xx_is_not_ok(): opener = make_opener(status=503, body=b"oops") res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is False assert "503" in (res.error or "") def test_fetch_httperror_5xx_is_not_ok(): opener = make_opener(exc=http_error(502)) res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is False def test_fetch_unreadable_body_is_not_ok(): opener = make_opener(status=200, body=b"not-json{{{") res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is False def test_fetch_good_body_is_ok(): opener = make_opener(status=200, body=b'{"schema_version":1,"stages":[]}') res = orch_mod.fetch_metrics("http://x/metrics", 1.0, opener=opener) assert res.ok is True assert res.envelope["schema_version"] == 1 def test_orch_down_signal_debounce_then_alert(): cfg = _cfg() # Single transient failure -> NOT active (does not flap). assert orch_down_signal(1, cfg, "timeout").active is False assert orch_down_signal(2, cfg, "timeout").active is False # K-th consecutive failure -> active alarm. sig = orch_down_signal(3, cfg, "timeout") assert sig.active is True assert sig.key == "orch_down" assert "не отвечает" in sig.detail