feat: implement Fitbit OAuth, Garmin MFA, and optimize segment discovery

- Add Fitbit authentication flow (save credentials, OAuth callback handling)
- Implement Garmin MFA support with successful session/cookie handling
- Optimize segment discovery with new sampling and activity query services
- Refactor database session management in discovery API for better testability
- Enhance activity data parsing for charts and analysis
- Update tests to use testcontainers and proper dependency injection
- Clean up repository by ignoring and removing tracked transient files (.pyc, .db)
This commit is contained in:
2026-01-16 15:35:26 -08:00
parent 45dbc32295
commit d1cfd0fd8e
217 changed files with 1795 additions and 922 deletions

View File

@@ -0,0 +1,39 @@
import pytest
from unittest.mock import MagicMock, patch
from src.services.bike_matching import run_matching_for_all
from src.models.activity import Activity
from src.models.bike_setup import BikeSetup
def test_bike_matching_job_updates_progress():
"""
Verify that run_matching_for_all calls job_manager.update_job
when a job_id is provided.
"""
mock_db = MagicMock()
# Mock Activities
activities = [
Activity(id=1, activity_type="cycling", bike_setup_id=None, bike_match_confidence=None),
Activity(id=2, activity_type="cycling", bike_setup_id=None, bike_match_confidence=None)
]
# Setup Query chain
mock_db.query.return_value.filter.return_value.all.return_value = activities
with patch("src.services.bike_matching.process_activity_matching") as mock_process:
with patch("src.services.job_manager.job_manager") as mock_job_manager:
# Important: Ensure expecting cancellation returns False, otherwise loop breaks
mock_job_manager.should_cancel.return_value = False
run_matching_for_all(mock_db, job_id="test-job-123")
# Verify update_job called at start
mock_job_manager.update_job.assert_any_call("test-job-123", message="Found 2 candidates. Matching...", progress=0)
# Verify update_job called during loop (progress)
# 2 items, index 0 satisfies % 10 == 0
mock_job_manager.update_job.assert_any_call("test-job-123", progress=0)
# Verify process was called
assert mock_process.call_count == 2