Remove all authentication (login, sessions, bcrypt, itsdangerous) since the app runs on a private homelab LAN. Replace with a profile picker landing page and cookie-based profile selection (1-year expiry). - Add Alembic migration to drop password_hash/is_admin columns - Delete auth service, auth routes, login template, and auth tests - Rewrite app/utils/auth.py with NoProfileSelectedError and require_active_profile dependency - Add profile creation flow (GET/POST /profiles/create) - Rewrite home page as profile picker with card layout - Update all route files to use profile dependency instead of admin auth - Remove bcrypt and itsdangerous from requirements - Remove admin_username/admin_password from config - Update all tests for new profile-based access model Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
"""Tests for the AnalyticsService class."""
|
|
|
|
from datetime import date, timedelta
|
|
|
|
from sqlmodel import SQLModel, Session, create_engine
|
|
|
|
from app.models.user import User
|
|
from app.models.exercise import Exercise
|
|
from app.models.workout_day import WorkoutDay
|
|
from app.models.workout_session import WorkoutSession
|
|
from app.models.workout_log import WorkoutLog
|
|
from app.services.analytics_service import AnalyticsService
|
|
|
|
|
|
class TestAnalyticsService:
|
|
"""Tests for analytics data aggregation."""
|
|
|
|
def _setup(self):
|
|
"""Create DB with sessions and logs for analytics."""
|
|
engine = create_engine("sqlite:///:memory:")
|
|
SQLModel.metadata.create_all(engine)
|
|
session = Session(engine)
|
|
|
|
user = User(username="phil", display_name="Phillip")
|
|
day = WorkoutDay(name="Push", day_number=1, description="Push day")
|
|
exercise = Exercise(
|
|
name="DB Chest Press", muscle_group="Chest",
|
|
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)
|
|
|
|
# Create 3 sessions over 3 weeks
|
|
for week in range(3):
|
|
ws = WorkoutSession(
|
|
user_id=user.id, workout_day_id=day.id,
|
|
date=date.today() - timedelta(days=(2 - week) * 7),
|
|
)
|
|
session.add(ws)
|
|
session.commit()
|
|
session.refresh(ws)
|
|
|
|
for set_num in range(1, 4):
|
|
session.add(WorkoutLog(
|
|
session_id=ws.id, exercise_id=exercise.id,
|
|
set_number=set_num,
|
|
reps_completed=8 + week,
|
|
weight_used="30 lbs",
|
|
felt_easy=(week == 2),
|
|
))
|
|
session.commit()
|
|
|
|
service = AnalyticsService(session)
|
|
return session, service, user, exercise
|
|
|
|
def test_get_total_sessions(self) -> None:
|
|
"""Should return the total number of sessions for a user."""
|
|
session, service, user, exercise = self._setup()
|
|
stats = service.get_user_stats(user.id)
|
|
assert stats["total_sessions"] == 3
|
|
session.close()
|
|
|
|
def test_get_total_volume(self) -> None:
|
|
"""Should calculate total volume (sets x reps x weight)."""
|
|
session, service, user, exercise = self._setup()
|
|
stats = service.get_user_stats(user.id)
|
|
assert stats["total_volume"] > 0
|
|
session.close()
|
|
|
|
def test_get_workout_streak(self) -> None:
|
|
"""Should calculate consecutive workout weeks."""
|
|
session, service, user, exercise = self._setup()
|
|
stats = service.get_user_stats(user.id)
|
|
assert stats["current_streak"] >= 1
|
|
session.close()
|
|
|
|
def test_get_exercise_progress_data(self) -> None:
|
|
"""Should return chart-ready data for an exercise."""
|
|
session, service, user, exercise = self._setup()
|
|
data = service.get_exercise_progress(user.id, exercise.id)
|
|
assert "dates" in data
|
|
assert "reps" in data
|
|
assert "weights" in data
|
|
assert len(data["dates"]) == 3
|
|
session.close()
|
|
|
|
def test_get_volume_by_day(self) -> None:
|
|
"""Should return volume per workout day for charts."""
|
|
session, service, user, exercise = self._setup()
|
|
data = service.get_volume_by_day(user.id)
|
|
assert "Push" in data
|
|
assert data["Push"] > 0
|
|
session.close()
|