/
pa1ch
/
FitAssistant
Обзор
Документация
Войти
/
pa1ch
/
FitAssistant
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
tests/integration/api/test_workout_draft.py
149 строк
6 KB
Pavel Chugaev
feat(api): оценка тренировки (rating/tags/comment) и время+длительность в облачном черновике
16 июл 2026, 08:28
16 июл 2026, 08:28
1a76494
Код
Авторство
О чём код?
"""The /workouts/draft endpoint and the draft's exclusion from normal lists.""" from datetime import timedelta from app.services.exercise import get_all_exercises from app.services.workout import DraftExercise, DraftSet, save_draft async def test_draft_endpoint_lifecycle(client, session, user, seeded_exercises): # No open draft yet → null. r = await client.get("/api/v1/workouts/draft") assert r.status_code == 200 assert r.json() is None # A draft captured by the bot (simulated via the service on the shared session). ids = list((await get_all_exercises(session, user_id=user.id)).keys()) await save_draft( session, user.id, user.today(), [DraftExercise(exercise_id=ids[0], sets=[DraftSet(reps=10, weight_kg=80)])], ) await session.commit() # The endpoint now returns it. body = (await client.get("/api/v1/workouts/draft")).json() assert body is not None assert body["status"] == "draft" assert len(body["exercises"]) == 1 # ...but it's hidden from the normal history list. listed = (await client.get("/api/v1/workouts")).json() assert all(w["status"] != "draft" for w in listed) # Finishing it via the existing complete route makes it a real workout. r = await client.post(f"/api/v1/workouts/{body['id']}/complete") assert r.status_code == 200 assert r.json()["status"] == "completed" # No open draft remains. assert (await client.get("/api/v1/workouts/draft")).json() is None async def test_put_draft_upserts_and_carries_notes(client, session, user, seeded_exercises): ids = list((await get_all_exercises(session, user_id=user.id)).keys()) today = user.today().isoformat() # First save via the web form endpoint. r = await client.put( "/api/v1/workouts/draft", json={ "date": today, "notes": "лёгкая", "exercises": [{"exercise_id": ids[0], "sets": [{"reps": 10, "weight_kg": 80}]}], }, ) assert r.status_code == 200 body = r.json() assert body["status"] == "draft" assert body["notes"] == "лёгкая" assert len(body["exercises"]) == 1 assert "updated_at" in body # Saving again replaces the draft wholesale (still one open draft for today). r = await client.put( "/api/v1/workouts/draft", json={ "date": today, "notes": "лёгкая", "exercises": [ {"exercise_id": ids[0], "sets": [{"reps": 12, "weight_kg": 82.5}]}, {"exercise_id": ids[1], "sets": [{"reps": 8, "weight_kg": None}]}, ], }, ) assert r.status_code == 200 assert len(r.json()["exercises"]) == 2 assert (await client.get("/api/v1/workouts")).json() == [] # still hidden from history # A later voice capture merges into the SAME draft and must not wipe the note. await save_draft( session, user.id, user.today(), [DraftExercise(exercise_id=ids[0], sets=[DraftSet(reps=10, weight_kg=80)])], ) await session.commit() assert (await client.get("/api/v1/workouts/draft")).json()["notes"] == "лёгкая" async def test_draft_is_per_date(client, session, user, seeded_exercises): ids = list((await get_all_exercises(session, user_id=user.id)).keys()) today = user.today() future = today + timedelta(days=7) # Two independent open drafts: one for today, one for a future planning date. await client.put( "/api/v1/workouts/draft", json={"date": today.isoformat(), "exercises": [{"exercise_id": ids[0], "sets": []}]}, ) await client.put( "/api/v1/workouts/draft", json={"date": future.isoformat(), "exercises": [{"exercise_id": ids[1], "sets": []}]}, ) # GET without a date defaults to today; ?date= targets the other slot. assert (await client.get("/api/v1/workouts/draft")).json()["date"] == today.isoformat() r = await client.get(f"/api/v1/workouts/draft?date={future.isoformat()}") assert r.json()["date"] == future.isoformat() assert r.json()["exercises"][0]["exercise"]["id"] == ids[1] # Discarding today's draft leaves the future one intact. assert (await client.delete("/api/v1/workouts/draft")).status_code == 204 assert (await client.get("/api/v1/workouts/draft")).json() is None assert ( await client.get(f"/api/v1/workouts/draft?date={future.isoformat()}") ).json() is not None async def test_put_draft_roundtrips_time_fields(client, session, user, seeded_exercises): ids = list((await get_all_exercises(session, user_id=user.id)).keys()) today = user.today().isoformat() payload = { "date": today, "start_time": "18:30:00", "duration_minutes": 45, "exercises": [{"exercise_id": ids[0], "sets": [{"reps": 10}]}], } r = await client.put("/api/v1/workouts/draft", json=payload) assert r.status_code == 200 assert r.json()["start_time"] == "18:30:00" assert r.json()["duration_minutes"] == 45 body = (await client.get(f"/api/v1/workouts/draft?date={today}")).json() assert body["start_time"] == "18:30:00" assert body["duration_minutes"] == 45 # An agent-style save on the shared session (no time fields) keeps them. await save_draft( session, user.id, user.today(), [DraftExercise(exercise_id=ids[0], sets=[DraftSet(reps=8)])], ) await session.commit() body = (await client.get(f"/api/v1/workouts/draft?date={today}")).json() assert body["start_time"] == "18:30:00" assert body["duration_minutes"] == 45