"""The agent says what happened to its host (#5756): when it started and the host booted, when it stops and whether the host is going down with it, and how long it couldn't reach Steward.""" import subprocess from datetime import datetime from types import SimpleNamespace from unittest.mock import patch from plugins.host_agent import agent as a def test_events_ride_the_payload_only_when_there_are_some(): assert "events" not in a.build_payload([], "h", {}) assert a.build_payload([], "h", {}, events=[{"kind": "started"}])["events"] == [{"kind": "started"}] def test_started_says_when_the_host_booted(): with patch.object(a, "_read_file", return_value="3600.5 100.0\n"), \ patch.object(a.time, "time", return_value=1_800_000_000.0): event = a.started_event() assert event["kind"] == "started" assert datetime.fromisoformat(event["boot_at"]).timestamp() == 1_800_000_000.0 - 3600.5 def test_started_without_uptime_still_says_it_started(): with patch.object(a, "_read_file", side_effect=OSError): event = a.started_event() assert event["kind"] == "started" and "boot_at" not in event def test_the_host_is_going_down_when_systemd_says_stopping(): def run(out): return patch.object(a.subprocess, "run", return_value=SimpleNamespace(stdout=out)) with run("stopping\n"): assert a.host_shutting_down() is True with run("running\n"): assert a.host_shutting_down() is False with run("degraded\n"): assert a.host_shutting_down() is False with patch.object(a.subprocess, "run", side_effect=FileNotFoundError): assert a.host_shutting_down() is None with patch.object(a.subprocess, "run", side_effect=subprocess.TimeoutExpired("systemctl", 3)): assert a.host_shutting_down() is None def test_stopping_says_whether_the_host_goes_with_it(): with patch.object(a, "host_shutting_down", return_value=True): event = a.stopping_event() assert event["kind"] == "stopping" and event["host_shutdown"] is True and event["at"] def test_lost_contact_is_no_answer_or_a_proxy_s_5xx_not_steward_refusing(): assert a.lost_contact(None) and a.lost_contact(502) and a.lost_contact(503) assert not a.lost_contact(400) and not a.lost_contact(401) event = a.lost_contact_event("2026-10-14T10:00:00+00:00") assert event["kind"] == "lost_contact" and event["from"] == "2026-10-14T10:00:00+00:00" assert event["to"] > event["from"]