tests/test_worker_post_event.py
Rama main · réplica de lectura del repositorio original.
"""Worker-side _post_event helper: envelope shape, error swallowing."""
from pathlib import Path
from unittest.mock import MagicMock
import httpx
import jobd.worker.job_worker as job_worker
from jobd.worker.job_worker import _post_event
def test_post_event_builds_envelope_for_worker_shutdown():
client = MagicMock()
_post_event(client, "worker_shutdown", host="laptop")
client.post.assert_called_once()
args, kwargs = client.post.call_args
assert args[0] == "/events"
assert kwargs["timeout"] == 2.0
body = kwargs["json"]
assert body["source"] == "worker"
assert body["event"] == "worker_shutdown"
assert body["payload"] == {"host": "laptop"}
assert "job_id" not in body
assert "project" not in body
def test_post_event_includes_job_id_and_project_when_set():
client = MagicMock()
_post_event(
client,
"watchdog_fired",
job_id=123,
project="project-b",
host="desktop",
reason="idle_timeout",
threshold_s=600,
)
body = client.post.call_args.kwargs["json"]
assert body["job_id"] == 123
assert body["project"] == "project-b"
assert body["payload"] == {"host": "desktop", "reason": "idle_timeout", "threshold_s": 600}
def test_post_event_swallows_network_error_and_logs_it(caplog):
client = MagicMock()
client.post.side_effect = httpx.ConnectError("broker unreachable")
_post_event(client, "worker_shutdown", host="laptop")
# Asserted on the LOG RECORD, not raw stderr: the worker moved off print() so that
# systemd can record these at a real priority (see _JournalPriorityFormatter).
text = caplog.text
assert "/events POST failed" in text
assert "worker_shutdown" in text
assert any(r.levelname == "ERROR" for r in caplog.records), caplog.records
# No exception propagated.
def test_post_event_swallows_http_error_response_no_raise():
"""httpx.Client.post does not raise_for_status; even a 4xx response is silent.
The helper does not call raise_for_status itself, so the worker hot path
cannot blow up on a malformed audit event."""
client = MagicMock()
response = MagicMock()
response.status_code = 422
client.post.return_value = response
_post_event(client, "watchdog_fired", host="laptop", reason="x", threshold_s=1)
# No exception means pass.
def test_post_event_is_called_at_three_trigger_sites_in_source():
"""Sentinel: confirm wiring landed at watchdog wall-timeout, watchdog
idle-timeout, and shutdown handler. If a refactor removes one of these,
this test fails loud rather than silently dropping audit coverage."""
src = Path(job_worker.__file__).read_text()
assert src.count("_post_event(") >= 4, (
"expected at least 4 _post_event calls (helper def + 3 trigger sites); "
f"found {src.count('_post_event(')}"
)
# Specific content checks: each of the three reasons/events should appear
# alongside _post_event in the source.
assert '"watchdog_fired"' in src
assert '"wall_timeout"' in src
assert '"idle_timeout"' in src
assert '"worker_shutdown"' in src