testsAndMisc/python_pkg/screen_locker/tests/test_verify_data.py
Krzysztof kuhy Rudnicki 2545d72710 test: achieve 100% branch coverage across all python_pkg packages
- Add comprehensive tests for all packages (3572 tests, 100% branch coverage)
- Split oversized test files to stay under 500-line limit
- Add per-file ruff ignores for test-appropriate suppressions
- Fix _cache_decks.py to properly convert JSON lists to tuples
- Add session-scoped conftest fixture for logging handler cleanup (Python 3.14)
- Update ruff pre-commit hook to v0.15.2
- Add codespell ignore words for test data
- Add generated output files to .gitignore
2026-03-21 17:51:36 +01:00

406 lines
13 KiB
Python

"""Tests for running and strength data verification."""
from __future__ import annotations
from typing import TYPE_CHECKING
from unittest.mock import MagicMock
from python_pkg.screen_locker.tests.conftest import (
RunningData,
StrengthData,
create_locker,
setup_running_entries,
setup_strength_entries,
)
if TYPE_CHECKING:
from pathlib import Path
class TestVerifyRunningData:
"""Tests for verify_running_data method."""
def test_valid_running_data(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test valid running data triggers unlock attempt."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "25", "5"))
locker.log_file = tmp_path / "workout_log.json"
locker.workout_data = {"type": "running"}
object.__setattr__(locker, "_attempt_unlock", MagicMock())
locker.verify_running_data()
locker._attempt_unlock.assert_called_once()
def test_invalid_distance_zero(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test zero distance is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("0", "25", "5"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Distance" in locker.show_error.call_args[0][0]
def test_invalid_distance_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test distance over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("150", "600", "4"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Distance" in locker.show_error.call_args[0][0]
def test_invalid_time_zero(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test zero time is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "0", "5"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Time" in locker.show_error.call_args[0][0]
def test_invalid_time_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test time over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "700", "5"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Time" in locker.show_error.call_args[0][0]
def test_invalid_pace_zero(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test zero pace is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "25", "0"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Pace" in locker.show_error.call_args[0][0]
def test_invalid_pace_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test pace over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "25", "25"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Pace" in locker.show_error.call_args[0][0]
def test_pace_mismatch(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test pace mismatch is rejected."""
# 5km in 25 min should be 5 min/km, but we say 10 min/km
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("5", "25", "10"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "Pace doesn't match" in locker.show_error.call_args[0][0]
def test_invalid_number_format(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test non-numeric input is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_running_entries(locker, RunningData("abc", "25", "5"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_running_data()
locker.show_error.assert_called_once()
assert "valid numbers" in locker.show_error.call_args[0][0]
class TestVerifyStrengthData:
"""Tests for verify_strength_data method."""
def test_valid_strength_data(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test valid strength data triggers unlock attempt."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "10", "50", "1500"))
locker.log_file = tmp_path / "workout_log.json"
locker.workout_data = {"type": "strength"}
object.__setattr__(locker, "_attempt_unlock", MagicMock())
locker.verify_strength_data()
locker._attempt_unlock.assert_called_once()
def test_valid_multiple_exercises(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test valid data with multiple exercises."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(
locker,
StrengthData("Squat, Bench Press", "3, 3", "10, 8", "50, 40", "2460"),
)
locker.log_file = tmp_path / "workout_log.json"
locker.workout_data = {"type": "strength"}
object.__setattr__(locker, "_attempt_unlock", MagicMock())
locker.verify_strength_data()
locker._attempt_unlock.assert_called_once()
def test_mismatched_list_lengths(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test mismatched list lengths are rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(
locker,
StrengthData("Squat, Bench", "3", "10, 8", "50, 40", "2000"),
)
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "must match" in locker.show_error.call_args[0][0]
def test_short_exercise_name(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test short exercise names are rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Sq", "3", "10", "50", "1500"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "too short" in locker.show_error.call_args[0][0]
def test_invalid_sets_zero(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test zero sets is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "0", "10", "50", "0"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Sets" in locker.show_error.call_args[0][0]
def test_invalid_sets_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test sets over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "25", "10", "50", "12500"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Sets" in locker.show_error.call_args[0][0]
def test_invalid_reps_zero(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test zero reps is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "0", "50", "0"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Reps" in locker.show_error.call_args[0][0]
def test_invalid_reps_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test reps over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "150", "50", "22500"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Reps" in locker.show_error.call_args[0][0]
def test_invalid_weight_negative(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test negative weight is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "10", "-10", "-300"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Weights" in locker.show_error.call_args[0][0]
def test_invalid_weight_too_high(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test weight over max is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "10", "600", "18000"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Weights" in locker.show_error.call_args[0][0]
def test_total_weight_mismatch(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test total weight mismatch is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "3", "10", "50", "3000"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "Total weight doesn't match" in locker.show_error.call_args[0][0]
def test_invalid_format(
self,
mock_tk: MagicMock,
_mock_sys_exit: MagicMock,
tmp_path: Path,
) -> None:
"""Test invalid format is rejected."""
locker = create_locker(mock_tk, tmp_path)
setup_strength_entries(locker, StrengthData("Squat", "abc", "10", "50", "1500"))
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "valid data" in locker.show_error.call_args[0][0]
class TestVariableReps:
"""Tests for variable reps format in strength verification."""
def test_valid_variable_reps(
self, mock_tk: MagicMock, _mock_sys_exit: MagicMock, tmp_path: Path
) -> None:
"""Test valid variable reps with + separator."""
locker = create_locker(mock_tk, tmp_path)
# 3 sets, reps 12+11+12 (3 variable values matching 3 sets), weight 50
# Total = (12+11+12) * 50 = 1750
setup_strength_entries(
locker, StrengthData("Squat", "3", "12+11+12", "50", "1750")
)
locker.log_file = tmp_path / "workout_log.json"
locker.workout_data = {"type": "strength"}
object.__setattr__(locker, "_attempt_unlock", MagicMock())
locker.verify_strength_data()
locker._attempt_unlock.assert_called_once()
def test_variable_reps_count_mismatch(
self, mock_tk: MagicMock, _mock_sys_exit: MagicMock, tmp_path: Path
) -> None:
"""Test variable reps count not matching sets."""
locker = create_locker(mock_tk, tmp_path)
# 5 sets but only 3 variable reps
setup_strength_entries(
locker, StrengthData("Squat", "5", "12+11+12", "50", "1750")
)
object.__setattr__(locker, "show_error", MagicMock())
locker.verify_strength_data()
locker.show_error.assert_called_once()
assert "variable reps count" in locker.show_error.call_args[0][0]