screen-locker/screen_locker/tests/test_status_data_part3.py
Krzysztof kuhy Rudnicki 4a36b6b264 Fix mechanical pylint findings in tests
Booleaness asserts, hoisted imports, fixture(name=...) instead of
shadowing arguments, explicit encodings, snake_case names, comparison
of bound methods, dead protected-access disables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWYdwRAtMg7ki6TKxuU
2026-11-01 21:11:34 +01:00

166 lines
6.9 KiB
Python

"""Tests for gather_status's lock-suppression paths.
Split out of test_status_data_part2.py for the 250-line cap: these are the
cases where something (sick day, wake alarm, scheduled skip, relaxed day,
weekly minimum) means no lock is needed.
"""
from __future__ import annotations
from datetime import UTC, date, datetime
import json
from typing import TYPE_CHECKING
from unittest.mock import patch
import freedays
import sickdays
from screen_locker._status_data import format_summary_line, gather_status
from screen_locker.tests._sick_helpers import mark_sick
from screen_locker.tests.test_status_data import _files
if TYPE_CHECKING:
from pathlib import Path
# Fixed reference instant: Friday 2024-01-05, 12:00 UTC == 13:00 Europe/Warsaw.
# Outside the 05:00-09:00 early-bird window and not a Tue/Wed/Thu relaxed day,
# so lock-decision branches are fully deterministic regardless of wall clock.
_FRIDAY_NOON_UTC = datetime(2024, 1, 5, 12, 0, tzinfo=UTC)
# Monday of that same ISO week, for Mon-Wed shutdown-band assertions.
_MONDAY_NOON_UTC = datetime(2024, 1, 1, 12, 0, tzinfo=UTC)
class TestGatherStatusLockSuppression:
"""Conditions under which gather_status reports no lock is needed."""
def test_sick_day_marks_today_and_skips_lock(self, tmp_path: Path) -> None:
"""Sick day marks today and skips lock."""
files = _files(tmp_path)
mark_sick("2024-01-05")
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.today.is_sick_day is True
assert format_summary_line(snap).endswith("· sick day")
assert snap.lock_explanation.fired is False
assert snap.lock_explanation.stage == "sick_day"
def test_sick_day_keeping_the_workout_lock_still_locks(
self, tmp_path: Path
) -> None:
"""A kept workout lock is a normal day: not marked sick, not skipped."""
files = _files(tmp_path)
mark_sick("2024-01-05", keep=[sickdays.LOCKER_WORKOUT])
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.today.is_sick_day is False
assert not format_summary_line(snap).endswith("· sick day")
assert snap.lock_explanation.stage != "sick_day"
def test_wake_alarm_skip_stops_lock(self, tmp_path: Path) -> None:
"""Wake alarm skip stops lock."""
files = _files(tmp_path)
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=True
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.lock_explanation.fired is False
assert snap.lock_explanation.stage == "wake_alarm_skip"
def test_scheduled_skip_stops_lock(self, tmp_path: Path) -> None:
"""Scheduled skip stops lock."""
files = _files(tmp_path)
freedays.mark(
date(2024, 1, 5),
paths=freedays.Paths.under(tmp_path / "freedays"),
now=date(2024, 1, 5),
)
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.lock_explanation.stage == "scheduled_skip"
def test_relaxed_day_stops_lock(self, tmp_path: Path) -> None:
"""Tuesday is a relaxed day — is_relaxed_day derives it from `now`."""
files = _files(tmp_path)
tuesday_noon = datetime(2024, 1, 2, 12, 0, tzinfo=UTC)
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=tuesday_noon)
assert snap.lock_explanation.stage == "relaxed_day"
def test_weekly_minimum_met_stops_lock(self, tmp_path: Path) -> None:
# Avoid dating anything "today" (2024-01-05): an unsigned today entry
# trips already_logged before weekly_minimum_met, and does so only
# when no HMAC key is configured (env-dependent) -- Jan 4 holds two.
"""Weekly minimum met stops lock."""
files = _files(tmp_path)
files["log_file"].write_text(
json.dumps(
{
"2024-01-01": {"workout_data": {"type": "phone_verified"}},
"2024-01-02": {"workout_data": {"type": "phone_verified"}},
"2024-01-03": {"workout_data": {"type": "phone_verified"}},
# A gym session and a run on the same day: two genuinely
# separate workouts, so two credits. Deliberately NOT two
# phone_verified entries -- since the 2026-08-29 fix those
# share one StrongLifts credit slot per day, which is the
# whole point of the dedup.
"2024-01-04": [
{"workout_data": {"type": "phone_verified"}},
{"workout_data": {"type": "runnerup_verified"}},
],
}
)
)
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.week.counted_count == 5
assert not snap.week.remaining
assert not snap.week.extra
assert snap.lock_explanation.stage == "weekly_minimum_met"
def test_extra_workouts_beyond_minimum(self, tmp_path: Path) -> None:
"""Extra workouts beyond minimum."""
files = _files(tmp_path)
# Jan 1-4 one each, Jan 5 (today, Friday — the last day in-week without
# going into the future) holds two, for 6 counted total: 1 above the
# new minimum of 5. Also exercises multiple same-day entries each
# counting individually — which is why the pair is a gym session plus a
# run rather than two phone_verified entries: those are the two
# ingestion paths for ONE StrongLifts session and share a credit slot.
files["log_file"].write_text(
json.dumps(
{
"2024-01-01": {"workout_data": {"type": "phone_verified"}},
"2024-01-02": {"workout_data": {"type": "phone_verified"}},
"2024-01-03": {"workout_data": {"type": "phone_verified"}},
"2024-01-04": {"workout_data": {"type": "phone_verified"}},
"2024-01-05": [
{"workout_data": {"type": "phone_verified"}},
{"workout_data": {"type": "runnerup_verified"}},
],
}
)
)
with patch(
"screen_locker._status_data.has_workout_skip_today", return_value=False
):
snap = gather_status(**files, now=_FRIDAY_NOON_UTC)
assert snap.week.extra == 1
assert not snap.week.remaining