FitMop/backend/tests/test_garmin_sync_vo2.py

127 lines
4.6 KiB
Python

import json
import os
from datetime import date, timedelta
from unittest.mock import MagicMock, patch
import pytest
from garmin.sync import GarminSync
@pytest.fixture
def mock_client():
return MagicMock()
@pytest.fixture
def temp_storage(tmp_path):
return str(tmp_path / "data")
def test_dashboard_stats_vo2max_fallback(mock_client, temp_storage):
# Coverage for sync.py VO2Max logic
fixed_today = date(2026, 1, 1)
os.makedirs(temp_storage, exist_ok=True)
# 1. Running with VO2Max (Should be preferred)
with open(os.path.join(temp_storage, "activity_run.json"), "w") as f:
json.dump({
"activityId": 1,
"startTimeLocal": fixed_today.strftime("%Y-%m-%d %H:%M:%S"),
"activityType": {"typeKey": "running"},
"vo2MaxValue": 55
}, f)
with patch("garmin.sync.date") as mock_date:
mock_date.today.return_value = fixed_today
sync = GarminSync(mock_client, storage_dir=temp_storage)
stats = sync.get_dashboard_stats()
assert stats["vo2_max"] == 55
# 2. Cycling Only (Should pick cycling)
# Remove run
os.remove(os.path.join(temp_storage, "activity_run.json"))
with open(os.path.join(temp_storage, "activity_cycle.json"), "w") as f:
json.dump({
"activityId": 2,
"startTimeLocal": fixed_today.strftime("%Y-%m-%d %H:%M:%S"),
"activityType": {"typeKey": "cycling"},
"vo2MaxCyclingValue": 45
}, f)
with patch("garmin.sync.date") as mock_date:
mock_date.today.return_value = fixed_today
sync = GarminSync(mock_client, storage_dir=temp_storage)
stats = sync.get_dashboard_stats()
assert stats["vo2_max"] == 45
# 3. Mixed (Running has no VO2, Cycling does -> Should fallback to Cycling)
with open(os.path.join(temp_storage, "activity_run_no_vo2.json"), "w") as f:
json.dump({
"activityId": 3,
"startTimeLocal": (fixed_today + timedelta(hours=1)).strftime("%Y-%m-%d %H:%M:%S"), # Newer
"activityType": {"typeKey": "running"}
# No vo2MaxValue
}, f)
with patch("garmin.sync.date") as mock_date:
mock_date.today.return_value = fixed_today
sync = GarminSync(mock_client, storage_dir=temp_storage)
stats = sync.get_dashboard_stats()
assert stats["vo2_max"] == 45 # From cycling
def test_dashboard_stats_vo2max_deep_scan(mock_client, temp_storage):
# Coverage for sync.py: VO2Max found outside top 5 (deep scan)
fixed_today = date(2026, 1, 1)
os.makedirs(temp_storage, exist_ok=True)
# Create 6 activities. Top 5 have no VO2. 6th has it.
for i in range(6):
with open(os.path.join(temp_storage, f"activity_{i}.json"), "w") as f:
# i=0 is newest. i=5 is oldest.
# We want headers to be sorted by date desc.
# So activity_0 is today. activity_5 is today-5.
act_date = fixed_today - timedelta(days=i)
data = {
"activityId": i,
"startTimeLocal": act_date.strftime("%Y-%m-%d %H:%M:%S"),
"activityType": {"typeKey": "running"},
"duration": 1800
}
if i == 5:
# The 6th activity (oldest in this set) has the VO2
data["vo2MaxValue"] = 52
json.dump(data, f)
with patch("garmin.sync.date") as mock_date:
mock_date.today.return_value = fixed_today
sync = GarminSync(mock_client, storage_dir=temp_storage)
stats = sync.get_dashboard_stats()
assert stats["vo2_max"] == 52
def test_dashboard_stats_vo2max_cycling_deep_scan(mock_client, temp_storage):
# Coverage for sync.py: Cycling VO2Max found outside top 5
fixed_today = date(2026, 1, 1)
os.makedirs(temp_storage, exist_ok=True)
for i in range(6):
with open(os.path.join(temp_storage, f"activity_{i}.json"), "w") as f:
act_date = fixed_today - timedelta(days=i)
data = {
"activityId": i,
"startTimeLocal": act_date.strftime("%Y-%m-%d %H:%M:%S"),
"activityType": {"typeKey": "cycling"},
"duration": 1800
}
if i == 5:
# The 6th activity
data["vo2MaxCyclingValue"] = 48
json.dump(data, f)
with patch("garmin.sync.date") as mock_date:
mock_date.today.return_value = fixed_today
sync = GarminSync(mock_client, storage_dir=temp_storage)
stats = sync.get_dashboard_stats()
assert stats["vo2_max"] == 48