import socket import pytest from edge import watchdog from edge.daemon import run_loop # 타깃은 Pi(Linux). AF_UNIX가 없는 개발 머신(Windows)에서는 실소켓 검증을 건너뛴다 — # sd_notify가 그 환경에서 예외 없이 False를 반환하는지는 아래 테스트가 따로 지킨다. needs_unix_socket = pytest.mark.skipif( not hasattr(socket, "AF_UNIX"), reason="AF_UNIX unavailable on this platform") class _Runtime: def __init__(self): self.steps = 0 def step(self, now): self.steps += 1 return [] def apply_zones(self, zones): pass class _Client: def fetch_config(self): return None def send_heartbeat(self, metrics): return True class _Queue: def flush(self): return 0 def pending(self): return 0 def test_sd_notify_is_a_noop_without_systemd(monkeypatch): # 개발 머신·테스트에서는 NOTIFY_SOCKET이 없다. 여기서 예외가 나면 데몬이 못 뜬다. monkeypatch.delenv("NOTIFY_SOCKET", raising=False) assert watchdog.ping() is False assert watchdog.ready() is False def test_sd_notify_never_raises_on_a_broken_socket(monkeypatch): # 소켓이 사라져도(재시작 중 등), 또 AF_UNIX가 없는 플랫폼에서도 텔레메트리 실패가 # 감지를 멈춰서는 안 된다. 예외 대신 False. monkeypatch.setenv("NOTIFY_SOCKET", "/nonexistent/socket/path") assert watchdog.ping() is False @needs_unix_socket def test_sd_notify_sends_the_datagram(tmp_path, monkeypatch): path = str(tmp_path / "notify.sock") srv = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) srv.bind(path) try: monkeypatch.setenv("NOTIFY_SOCKET", path) assert watchdog.ping() is True assert srv.recv(64) == b"WATCHDOG=1" finally: srv.close() def test_run_loop_pings_the_watchdog_every_tick(): # 워치독이 없으면 '살아 있지만 멈춘' 데몬은 자동 복구되지 않는다. pings = [] run_loop(_Runtime(), _Client(), _Queue(), clock=lambda: 0.0, sleep=lambda s: None, iterations=5, notify=lambda: pings.append(1)) assert len(pings) == 5 def test_watchdog_failure_does_not_stop_the_loop(): rt = _Runtime() def boom(): raise OSError("notify socket gone") run_loop(rt, _Client(), _Queue(), clock=lambda: 0.0, sleep=lambda s: None, iterations=3, notify=boom, log=lambda m, e=None: None) assert rt.steps == 3 # 감지는 계속 돈다