screen-locker/screen_locker/tests/test_decision_log_modes.py
Krzysztof kuhy Rudnicki 1d5965b9a7
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Close the four gaps that let a decided lock never reach the screen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-30 15:24:45 +02:00

92 lines
3.6 KiB
Python

"""Tests for the run-modes that never reach the lock decision.
Split out of test_decision_log.py for the 250-line cap. These cover the two
records that keep "this run did not decide" distinguishable from "this run
decided not to lock" -- the confusion that hid a 13-day enforcement outage.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from unittest.mock import patch
from screen_locker._decision_log import (
read_decisions,
record_no_decision,
record_run_aborted,
)
if TYPE_CHECKING:
from pathlib import Path
import pytest
_PKG = "screen_locker._decision_log"
class TestRecordNoDecision:
"""Modes that never evaluate the lock must be distinguishable."""
def test_records_mode_and_null_lock(self, tmp_path: Path) -> None:
"""'did not decide' must not look like 'decided not to lock'."""
target = tmp_path / "d.jsonl"
record_no_decision("--sync-only", log_file=target)
(record,) = read_decisions(log_file=target)
assert record["locked"] is None
assert record["reason"] == "mode_makes_no_decision"
assert record["mode"] == "--sync-only"
def test_it_reports_what_the_locker_was_doing(self, tmp_path: Path) -> None:
"""The every-15-minutes heartbeat is where this gets observed.
systemd cannot start a second instance of an already-active
Type=simple unit, so on 2026-08-30 the 09:30-14:00 timer triggers were
silent no-ops and these sync lines were the only records in the
window -- saying nothing about the run that had decided to lock at
09:01 and was still waiting behind wake_alarm.
"""
target = tmp_path / "d.jsonl"
with (
patch(f"{_PKG}.locker_unit_active", return_value=True),
patch(
f"{_PKG}.read_queue_wait",
return_value={"blocked_by": ["wake_alarm"], "elapsed_seconds": 9000},
),
):
record_no_decision("--sync-only", log_file=target)
(record,) = read_decisions(log_file=target)
assert record["locker_running"] is True
assert record["queued_behind"] == ["wake_alarm"]
def test_an_unqueued_run_omits_the_blocker_field(self, tmp_path: Path) -> None:
"""No blocker is the normal case; it must not add noise to every row."""
target = tmp_path / "d.jsonl"
with (
patch(f"{_PKG}.locker_unit_active", return_value=False),
patch(f"{_PKG}.read_queue_wait", return_value=None),
):
record_no_decision("--sync-only", log_file=target)
(record,) = read_decisions(log_file=target)
assert "queued_behind" not in record
assert record["locker_running"] is False
class TestRecordRunAborted:
"""A run that meant to decide and could not is its own outcome."""
def test_it_records_the_reason_and_the_detail(self, tmp_path: Path) -> None:
"""1695 runs died at 02:00 leaving only a stack trace behind."""
target = tmp_path / "d.jsonl"
record_run_aborted("no_display", "No usable X display.", log_file=target)
(record,) = read_decisions(log_file=target)
assert record["locked"] is None
assert record["reason"] == "no_display"
assert record["detail"] == "No usable X display."
def test_it_is_loud(self, tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
"""Unlike the sync modes, this is never an expected outcome."""
record_run_aborted(
"no_display", "No usable X display.", log_file=tmp_path / "d"
)
assert any(r.levelname == "WARNING" for r in caplog.records)