screen-locker/screen_locker/tests/test_poke_sandbox.py
2026-10-09 19:05:51 +02:00

88 lines
3.0 KiB
Python

"""A sandbox poke is verified and answered, but writes and credits nothing."""
from __future__ import annotations
from typing import TYPE_CHECKING
from unittest.mock import MagicMock
import pytest
from screen_locker import _poke_credit, _poke_server
from screen_locker._poke_credit import PokeLocker
from screen_locker._poke_wire import PokeRequest
if TYPE_CHECKING:
from pathlib import Path
_LOCKER_STEPS = (
"start_the_day",
"credit_written",
"_write_shutdown_config",
"_adjust_shutdown_time_later",
"_apply_credit_for_written_entry",
)
def _request(*, sandbox: bool) -> PokeRequest:
return PokeRequest(
sent_at_ms=1,
nonce="n",
sandbox=sandbox,
record_id="rec-1",
payload={"date": "2026-10-09"},
)
@pytest.fixture
def pc(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, MagicMock]:
"""A real PokeLocker on a tmp log with every state-changing step spied."""
def headless(locker_cls: type[PokeLocker]) -> PokeLocker:
locker = object.__new__(locker_cls)
locker.log_file = tmp_path / "log.json"
return locker
spies = {name: MagicMock() for name in (*_LOCKER_STEPS, "ingest", "credit")}
for name in _LOCKER_STEPS:
monkeypatch.setattr(PokeLocker, name, spies[name])
monkeypatch.setattr(_poke_credit, "_headless_locker", headless)
monkeypatch.setattr(_poke_credit, "ingest_session_records", spies["ingest"])
monkeypatch.setattr(_poke_credit, "credit_session", spies["credit"])
monkeypatch.setattr(_poke_credit, "current_shutdown", lambda: "20:30")
monkeypatch.setattr(_poke_credit, "current_gaming_minutes", lambda _p: 75)
return spies
def test_sandbox_replies_ok_with_the_snapshot_and_nothing_credited(
pc: dict[str, MagicMock],
) -> None:
reply = _poke_server._process(_request(sandbox=True))
assert reply["ok"] is True
assert reply["sandbox"] is True
assert reply["credited"] is False
assert reply["duplicate"] is False
assert reply["shutdown"] == "20:30"
assert reply["gaming_budget_minutes"] == 75
assert "nothing written or credited" in str(reply["reason"])
def test_sandbox_never_logs_credits_or_touches_the_shutdown_config(
pc: dict[str, MagicMock], tmp_path: Path
) -> None:
before = sorted(p.name for p in tmp_path.iterdir())
_poke_server._process(_request(sandbox=True))
for name, spy in pc.items():
assert not spy.called, name
assert sorted(p.name for p in tmp_path.iterdir()) == before
assert not (tmp_path / "log.json").exists()
def test_a_real_poke_does_reach_the_credit_step(pc: dict[str, MagicMock]) -> None:
"""Guards the guard: the spies above are wired to the path a real poke takes."""
pc["credit"].return_value = _poke_credit.PokeOutcome(
ok=True, credited=True, duplicate=False, reason="workout credited"
)
reply = _poke_server._process(_request(sandbox=False))
assert reply["credited"] is True
pc["credit"].assert_called_once_with("rec-1", {"date": "2026-10-09"})