import json import os from datetime import date 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_get_last_sync_date(mock_client, temp_storage): os.makedirs(temp_storage, exist_ok=True) sync = GarminSync(mock_client, storage_dir=temp_storage) # 1. No files assert sync.get_last_sync_date() is None # 2. Add files today = date(2025, 12, 12) with open(os.path.join(temp_storage, "activity_1.json"), "w") as f: json.dump({"activityId": 1, "startTimeLocal": "2025-12-10 10:00:00"}, f) with open(os.path.join(temp_storage, "activity_2.json"), "w") as f: json.dump({"activityId": 2, "startTimeLocal": "2025-12-12 10:00:00"}, f) # 3. Invalid Date with open(os.path.join(temp_storage, "activity_bad.json"), "w") as f: json.dump({"activityId": 3, "startTimeLocal": "invalid-date"}, f) assert sync.get_last_sync_date() == today def test_sync_smart_no_local(mock_client, temp_storage): os.makedirs(temp_storage, exist_ok=True) sync = GarminSync(mock_client, storage_dir=temp_storage) with patch.object(sync, 'sync_activities', return_value=10) as mock_sync_act: count = sync.sync_smart() assert count == 10 mock_sync_act.assert_called_with(days=365) def test_sync_smart_incremental(mock_client, temp_storage): os.makedirs(temp_storage, exist_ok=True) sync = GarminSync(mock_client, storage_dir=temp_storage) # Latest is 2 days ago mock_today = date(2025, 1, 5) last_sync = date(2025, 1, 3) with open(os.path.join(temp_storage, "activity_1.json"), "w") as f: json.dump({"activityId": 1, "startTimeLocal": f"{last_sync} 10:00:00"}, f) with patch("garmin.sync.date") as mock_date_cls: mock_date_cls.today.return_value = mock_today # Need to allow date constructor usage inside sync.py if used # But Sync.py imports date, datetime separately. # When we patch 'garmin.sync.date', we replace the class 'date' imported in that module # So date.today() is mocked. # But side effect: date(2025,1,5) might fail if we mocked the constructor? # Usually today is a classmethod. # Simpler: sync_smart calculates days = (today - latest_date).days # If today=5th, latest=3rd. diff = 2. with patch.object(sync, 'sync_activities', return_value=5) as mock_sync_act: sync.sync_smart() mock_sync_act.assert_called_with(days=2)