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