Files
SneakySwole/tests/test_models.py
Phillip Tarrant 52e48f8ed4 feat: replace wk1/wk4 targets with 6→8→10→12 rep ladder progression
Simplifies the progression model to a universal rep ladder: every exercise
follows 6→8→10→12 reps at current weight, then +5 lbs and reset to 6.
Replaces per-user wk1/wk4 rep and weight targets with a single
starting_weight field.

- Add Alembic migration to drop wk1_reps/wk4_reps/wk1_weight/wk4_weight,
  add starting_weight (migrated from wk1_weight)
- Run Alembic migrations on app startup instead of create_all, with
  auto-detection and stamping for legacy databases
- Include alembic/ and alembic.ini in Docker image
- Rewrite progression_service.get_suggestion() with ladder logic:
  climb, hold, weight_increase, hold_at_top, deload
- Replace wk1/wk4 grid in exercise cards with rep ladder progress bar
- Add color-coded progression badges by type
- Change weight log input from text to number with pre-filled suggestion
- Normalize weight input in routes (0→BW, bare number→N lbs)
- Remove schedule page (route, template, nav link, tests)
- Simplify user_programs.yaml from 4 fields to 1 per exercise
- Update all tests for new schema and progression logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:57:02 -05:00

223 lines
7.7 KiB
Python

"""Tests for SQLModel model definitions."""
from datetime import datetime
from sqlmodel import SQLModel, Session, create_engine
from app.models.user import User
from app.models.exercise import Exercise
from app.models.warmup import Warmup
from app.models.workout_day import WorkoutDay
from app.models.user_exercise_program import UserExerciseProgram
from app.models.workout_session import WorkoutSession
from app.models.workout_log import WorkoutLog
from app.models.progress_log import ProgressLog
class TestModels:
"""Tests that all models can be instantiated and persisted."""
def _create_engine(self):
"""Create an in-memory SQLite engine with all tables."""
engine = create_engine("sqlite:///:memory:")
SQLModel.metadata.create_all(engine)
return engine
def test_user_model_roundtrip(self) -> None:
"""User model should persist and retrieve correctly."""
engine = self._create_engine()
user = User(
username="testuser",
display_name="Test User",
height="6'0\"",
weight="200 lbs",
goals="Get strong",
)
with Session(engine) as session:
session.add(user)
session.commit()
session.refresh(user)
assert user.id is not None
assert user.username == "testuser"
assert isinstance(user.created_at, datetime)
def test_exercise_model_roundtrip(self) -> None:
"""Exercise model should persist and retrieve correctly."""
engine = self._create_engine()
exercise = Exercise(
name="DB Chest Press (Floor)",
muscle_group="Chest",
workout_day="Push",
sets=3,
tempo="3-1-2",
form_cues="Lie on the floor...",
)
with Session(engine) as session:
session.add(exercise)
session.commit()
session.refresh(exercise)
assert exercise.id is not None
assert exercise.name == "DB Chest Press (Floor)"
def test_warmup_model_roundtrip(self) -> None:
"""Warmup model should persist and retrieve correctly."""
engine = self._create_engine()
warmup = Warmup(
name="Cat / Cow",
type="Thoracic Mob",
reps="8 reps",
form_cues="Start on hands and knees...",
sort_order=1,
)
with Session(engine) as session:
session.add(warmup)
session.commit()
session.refresh(warmup)
assert warmup.id is not None
def test_workout_day_model_roundtrip(self) -> None:
"""WorkoutDay model should persist and retrieve correctly."""
engine = self._create_engine()
day = WorkoutDay(
name="Push",
day_number=1,
description="Chest, shoulders, triceps",
)
with Session(engine) as session:
session.add(day)
session.commit()
session.refresh(day)
assert day.id is not None
assert day.day_number == 1
def test_user_exercise_program_model_roundtrip(self) -> None:
"""UserExerciseProgram model should persist with FK references."""
engine = self._create_engine()
with Session(engine) as session:
user = User(username="u", display_name="U")
exercise = Exercise(
name="Test Ex", muscle_group="Test",
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
)
session.add(user)
session.add(exercise)
session.commit()
session.refresh(user)
session.refresh(exercise)
program = UserExerciseProgram(
user_id=user.id,
exercise_id=exercise.id,
starting_weight="30 lbs",
)
session.add(program)
session.commit()
session.refresh(program)
assert program.id is not None
assert program.user_id == user.id
def test_workout_session_model_roundtrip(self) -> None:
"""WorkoutSession model should persist correctly."""
engine = self._create_engine()
with Session(engine) as session:
user = User(username="u", display_name="U")
day = WorkoutDay(name="Push", day_number=1, description="Push day")
session.add(user)
session.add(day)
session.commit()
session.refresh(user)
session.refresh(day)
ws = WorkoutSession(
user_id=user.id,
workout_day_id=day.id,
date=datetime.utcnow().date(),
)
session.add(ws)
session.commit()
session.refresh(ws)
assert ws.id is not None
def test_workout_log_model_roundtrip(self) -> None:
"""WorkoutLog model should persist correctly."""
engine = self._create_engine()
with Session(engine) as session:
user = User(username="u", display_name="U")
day = WorkoutDay(name="Push", day_number=1, description="Push day")
exercise = Exercise(
name="Ex", muscle_group="Test",
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
)
session.add_all([user, day, exercise])
session.commit()
session.refresh(user)
session.refresh(day)
session.refresh(exercise)
ws = WorkoutSession(
user_id=user.id,
workout_day_id=day.id,
date=datetime.utcnow().date(),
)
session.add(ws)
session.commit()
session.refresh(ws)
log = WorkoutLog(
session_id=ws.id,
exercise_id=exercise.id,
set_number=1,
reps_completed=8,
weight_used="30 lbs",
felt_easy=False,
)
session.add(log)
session.commit()
session.refresh(log)
assert log.id is not None
assert log.felt_easy is False
def test_progress_log_model_roundtrip(self) -> None:
"""ProgressLog model should persist correctly."""
engine = self._create_engine()
with Session(engine) as session:
user = User(username="u", display_name="U")
exercise = Exercise(
name="Ex", muscle_group="Test",
workout_day="Push", sets=3, tempo="3-1-2", form_cues="..."
)
session.add_all([user, exercise])
session.commit()
session.refresh(user)
session.refresh(exercise)
pl = ProgressLog(
user_id=user.id,
exercise_id=exercise.id,
date=datetime.utcnow().date(),
suggested_reps=10,
suggested_weight="35 lbs",
actual_reps=10,
actual_weight="35 lbs",
progression_applied="reps_increase",
)
session.add(pl)
session.commit()
session.refresh(pl)
assert pl.id is not None
def test_all_models_importable_from_init(self) -> None:
"""All models should be importable from app.models."""
from app.models import (
User, Exercise, Warmup, WorkoutDay,
UserExerciseProgram, WorkoutSession, WorkoutLog, ProgressLog,
)
assert User is not None
assert Exercise is not None
assert Warmup is not None
assert WorkoutDay is not None
assert UserExerciseProgram is not None
assert WorkoutSession is not None
assert WorkoutLog is not None
assert ProgressLog is not None