Ctrl K
ComunidadGuías…

tests/test_worker_post_event.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""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