- 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)
65 lines
2.5 KiB
Python
65 lines
2.5 KiB
Python
|
|
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
from src.services.garmin.client import GarminClient
|
|
from garth.exc import GarthException
|
|
from sqlalchemy.orm import Session
|
|
|
|
def test_login_mfa_flow_crash():
|
|
# Mock DB session
|
|
mock_db = MagicMock(spec=Session)
|
|
mock_db.query.return_value.filter_by.return_value.first.return_value = None
|
|
|
|
# Mock garth
|
|
with patch('src.services.garmin.auth.garth') as mock_garth:
|
|
# 1. Setup mock to raise "needs-mfa" exception
|
|
mock_garth.login.side_effect = GarthException("Error: needs-mfa")
|
|
|
|
# 2. Setup mock client state that might be missing attributes
|
|
# This simulates a potential state where mfa_state is malformed or client is missing
|
|
mock_garth.client = MagicMock()
|
|
# Case A: mfa_state is None
|
|
mock_garth.client.mfa_state = None
|
|
|
|
client = GarminClient("testuser", "testpass")
|
|
|
|
# Expectation: calling login should NOT raise an unhandled exception
|
|
# It should catch GarthException and try to handle MFA.
|
|
# If it crashes here, we found the bug.
|
|
try:
|
|
status = client.login(mock_db)
|
|
print(f"Login status: {status}")
|
|
except Exception as e:
|
|
pytest.fail(f"Login raised unhandled exception: {e}")
|
|
|
|
def test_login_mfa_flow_success_structure():
|
|
# Test with CORRECT structure to verify what it expects
|
|
mock_db = MagicMock(spec=Session)
|
|
|
|
with patch('src.services.garmin.auth.garth') as mock_garth:
|
|
# Setup expected structure
|
|
mock_client_instance = MagicMock()
|
|
mock_client_instance._session.cookies.get_dict.return_value = {"cookie": "yum"}
|
|
# Link sess property to _session to match garth.Client behavior or ensure attribute exists
|
|
mock_client_instance.sess = mock_client_instance._session
|
|
mock_client_instance.domain = "garmin.com"
|
|
mock_client_instance.last_resp.text = "success"
|
|
mock_client_instance.last_resp.url = "http://garmin.com"
|
|
|
|
# Mock tuple return for success flow
|
|
mock_garth.login.return_value = ("needs_mfa", {
|
|
"signin_params": {"csrf": "token"},
|
|
"mfa": "state",
|
|
"client": mock_client_instance
|
|
})
|
|
mock_garth.login.side_effect = None
|
|
|
|
mock_garth.client.mfa_state = {
|
|
"signin_params": {"csrf": "token"},
|
|
"client": mock_client_instance
|
|
}
|
|
|
|
client = GarminClient("testuser", "testpass")
|
|
status = client.login(mock_db)
|
|
assert status == "mfa_required"
|