mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-11 21:53:54 +01:00
Real one-line docstrings stating what each test pins down. Files that would pass 250 lines moved whole classes into _part2 siblings (sick_panel, earned, poke_server, shutdown_reading, manual_workout_dialog). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWYdwRAtMg7ki6TKxuU
119 lines
4.6 KiB
Python
119 lines
4.6 KiB
Python
"""Tests for ``_earned``: an unreadable ledger or key fails closed to ``None``.
|
|
|
|
Companion to ``test_earned``; pins the cannot-check states and ``flat_answers``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
import earned_time
|
|
import pytest
|
|
|
|
from screen_locker import _earned
|
|
from screen_locker._earned import earned_today, flat_answers, flat_earners, ledger_file
|
|
from screen_locker.tests._earned_fixtures import credit, signing_key, write_ledger
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
from pathlib import Path
|
|
|
|
_LEDGERED = pytest.mark.parametrize(
|
|
"earner", [earned_time.LEETCODE, earned_time.READING], ids=lambda e: e.name
|
|
)
|
|
|
|
|
|
@pytest.fixture(name="key")
|
|
def key_fixture(tmp_path: Path) -> Iterator[Path]:
|
|
"""Install a valid HMAC signing key under the redirected key path."""
|
|
with signing_key(tmp_path) as path:
|
|
yield path
|
|
|
|
|
|
class TestCannotCheck:
|
|
"""Every unreadable state is None -- never a confident False -- and logged."""
|
|
|
|
@_LEDGERED
|
|
def test_missing_key(
|
|
self,
|
|
tmp_path: Path,
|
|
earner: earned_time.Earner,
|
|
caplog: pytest.LogCaptureFixture,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""The key is checked first: without it even "no ledger" is unknown."""
|
|
monkeypatch.setattr(_earned, "HMAC_KEY_FILE", tmp_path / "no.key")
|
|
with caplog.at_level(logging.WARNING):
|
|
assert earned_today(earner) is None
|
|
assert "Cannot read the integrity key" in caplog.text
|
|
|
|
def test_empty_key(self, tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
|
|
"""A whitespace-only key file is unreadable: the answer is None and a warning is logged."""
|
|
write_ledger(earned_time.LEETCODE, [credit(earned_time.LEETCODE)])
|
|
with signing_key(tmp_path, b" \n"), caplog.at_level(logging.WARNING):
|
|
assert earned_today(earned_time.LEETCODE) is None
|
|
assert "is empty" in caplog.text
|
|
|
|
def test_missing_leetcode_ledger_is_unknown(
|
|
self, key: Path, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""A missing LeetCode ledger is None (cannot check) and warned, not a plain no."""
|
|
with caplog.at_level(logging.WARNING):
|
|
assert earned_today(earned_time.LEETCODE) is None
|
|
assert "Cannot read the LeetCode ledger" in caplog.text
|
|
|
|
def test_missing_reading_ledger_is_a_plain_no(self, key: Path) -> None:
|
|
"""book-guard never ran: no reading, but no fault either."""
|
|
assert earned_today(earned_time.READING) is False
|
|
|
|
@_LEDGERED
|
|
def test_corrupt_ledger(
|
|
self,
|
|
key: Path,
|
|
earner: earned_time.Earner,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""A ledger that is not valid JSON yields None and a warning, never False."""
|
|
path = ledger_file(earner)
|
|
path.parent.mkdir(parents=True)
|
|
path.write_text("{not json")
|
|
with caplog.at_level(logging.WARNING):
|
|
assert earned_today(earner) is None
|
|
assert "not valid JSON" in caplog.text
|
|
|
|
|
|
@pytest.mark.usefixtures("leetcode_gate_off")
|
|
class TestFlatAnswers:
|
|
"""Groups the per-earner answer map built by ``flat_answers``."""
|
|
|
|
def test_every_flat_earner_answers_by_name(self, key: Path) -> None:
|
|
"""Every flat earner gets an entry by name, True only for the one with a credit today."""
|
|
write_ledger(earned_time.LEETCODE, [credit(earned_time.LEETCODE)])
|
|
nos = dict.fromkeys((e.name for e in flat_earners()), False)
|
|
assert flat_answers() == {**nos, "leetcode": True}
|
|
|
|
def test_an_unknown_answer_is_none_and_warned(
|
|
self,
|
|
tmp_path: Path,
|
|
caplog: pytest.LogCaptureFixture,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""An unreadable key makes every answer None and warns once per earner."""
|
|
monkeypatch.setattr(_earned, "HMAC_KEY_FILE", tmp_path / "no.key")
|
|
with caplog.at_level(logging.WARNING):
|
|
assert flat_answers() == dict.fromkeys(e.name for e in flat_earners())
|
|
for earner in flat_earners():
|
|
assert f"{earner.label} state could not be checked" in caplog.text
|
|
|
|
def test_a_no_is_not_warned(
|
|
self, key: Path, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""A definite no (empty ledger) produces no could-not-be-checked warning."""
|
|
write_ledger(earned_time.LEETCODE, [])
|
|
with caplog.at_level(logging.WARNING):
|
|
assert flat_answers() == dict.fromkeys(
|
|
(e.name for e in flat_earners()), False
|
|
)
|
|
assert "could not be checked" not in caplog.text
|