screen-locker/screen_locker/tests/test_manual_workout_features_part3.py
Krzysztof kuhy Rudnicki 3476dd3afd
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Split the last 8 test modules; scope pylint's test-inherent checks
Splits test_screen_lock_coverage_part2 via a shared _unlock_helpers.setup_unlock
factory rather than duplicating the fixture, and finishes the remaining
runnerup/manual-workout/status-view test modules under the 250-line cap.

Also splits the pylint hook in two. protected-access, unused-argument and
duplicate-code are how pytest and unit-testing private mixins work, not
defects, and a commit made entirely of test files could never clear the 8.0
batch score with them counted. Source keeps the full rule set — verified that
an injected protected-access is still reported there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EveB813BQ8abAP68VSqZ5Y
2026-08-15 19:40:32 +02:00

75 lines
2.7 KiB
Python

"""The manual-workout budget constants must match across Python and Dart.
Split out of test_manual_workout_features_part2.py for the 250-line cap.
"""
from __future__ import annotations
import pathlib
import re
from screen_locker._constants import (
MANUAL_WORKOUT_BUDGET_PER_7_DAYS,
MANUAL_WORKOUT_BUDGET_PER_30_DAYS,
)
# The Dart mirror of the budget caps; read at test time so drift on
# EITHER side fails, not just a local edit to the Python constants.
DART_MANUAL_WORKOUT = (
pathlib.Path(__file__).resolve().parents[2]
/ "stronglift_replacement"
/ "workout_app"
/ "lib"
/ "models"
/ "manual_workout.dart"
)
class TestCrossLanguageBudgetConstants:
"""Pins the budget caps against the Dart phone app's own source file.
The wire-format fixture in test_manual_workout.py pins payload key NAMES,
but the caps never travel on the wire, so it cannot catch them drifting —
and they did: 298daf0 raised Python 5 -> 10 while touching zero Dart files,
leaving the phone at 5 for weeks. The stricter side silently blocks a
workout the other accepts.
So this READS THE DART FILE rather than asserting a hardcoded literal on
each side. A test that compares a Python constant to a Python literal is
only a guard against a local edit; it stays green when the other language
drifts, which is the failure that actually happened. One test, either
direction.
"""
@staticmethod
def _dart_const(name: str) -> int:
"""Return the int assigned to `const int <name>` in the Dart model."""
source = DART_MANUAL_WORKOUT.read_text(encoding="utf-8")
match = re.search(rf"const int {name} = (\d+);", source)
assert match is not None, (
f"{name} not found in {DART_MANUAL_WORKOUT} — it was renamed or "
"removed; update this guard so the caps stay pinned."
)
return int(match.group(1))
def test_dart_model_file_exists(self) -> None:
"""A moved/renamed Dart model must fail loudly, not skip the guard."""
assert DART_MANUAL_WORKOUT.is_file(), (
f"{DART_MANUAL_WORKOUT} is missing; the budget caps are no longer "
"pinned across languages."
)
def test_per_7_days_matches_dart_mirror(self) -> None:
"""The 7-day cap must equal kManualWorkoutBudgetPer7Days."""
assert (
self._dart_const("kManualWorkoutBudgetPer7Days")
== MANUAL_WORKOUT_BUDGET_PER_7_DAYS
)
def test_per_30_days_matches_dart_mirror(self) -> None:
"""The 30-day cap must equal kManualWorkoutBudgetPer30Days."""
assert (
self._dart_const("kManualWorkoutBudgetPer30Days")
== MANUAL_WORKOUT_BUDGET_PER_30_DAYS
)