from datetime import datetime, timedelta from unittest.mock import AsyncMock, patch import pytest from backend.src.schemas import GarminCredentials from backend.src.services.garmin_auth_service import GarminAuthService @pytest.fixture def garmin_auth_service(): return GarminAuthService() @pytest.mark.asyncio async def test_initial_login_success(garmin_auth_service): username = "test@example.com" password = "password123" with patch('backend.src.services.garmin_auth_service.garth') as mock_garth: mock_garth.Client.return_value = AsyncMock() mock_garth.Client.return_value.login.return_value = None # garth.login doesn't return anything directly # Mock the attributes that would be set on the client after login mock_garth.Client.return_value.access_token = f"mock_access_token_for_{username}" mock_garth.Client.return_value.access_token_secret = f"mock_access_token_secret_for_{username}" mock_garth.Client.return_value.expires_in = 300 credentials = await garmin_auth_service.initial_login(username, password) assert credentials is not None assert credentials.garmin_username == username assert credentials.garmin_password_plaintext == password assert credentials.access_token.startswith("mock_access_token") assert credentials.access_token_secret.startswith("mock_access_token_secret") assert isinstance(credentials.token_expiration_date, datetime) assert credentials.token_expiration_date > datetime.utcnow() @pytest.mark.asyncio async def test_initial_login_failure(garmin_auth_service): username = "invalid@example.com" password = "wrongpassword" with patch('backend.src.services.garmin_auth_service.garth') as mock_garth: mock_garth.Client.return_value = AsyncMock() mock_garth.Client.return_value.login.side_effect = Exception("Garmin login failed") credentials = await garmin_auth_service.initial_login(username, password) assert credentials is None @pytest.mark.asyncio async def test_refresh_tokens_success(garmin_auth_service): credentials = GarminCredentials( garmin_username="test@example.com", garmin_password_plaintext="password123", access_token="old_access_token", access_token_secret="old_access_token_secret", token_expiration_date=datetime.utcnow() - timedelta(minutes=1) # Expired token ) with patch('backend.src.services.garmin_auth_service.garth') as mock_garth: mock_garth.Client.return_value = AsyncMock() mock_garth.Client.return_value.reauthorize.return_value = None mock_garth.Client.return_value.access_token = "refreshed_access_token" mock_garth.Client.return_value.access_token_secret = "refreshed_access_token_secret" mock_garth.Client.return_value.expires_in = 300 refreshed_credentials = await garmin_auth_service.refresh_tokens(credentials) assert refreshed_credentials is not None assert refreshed_credentials.garmin_username == credentials.garmin_username assert refreshed_credentials.access_token == "refreshed_access_token" assert refreshed_credentials.access_token_secret == "refreshed_access_token_secret" assert isinstance(refreshed_credentials.token_expiration_date, datetime) assert refreshed_credentials.token_expiration_date > datetime.utcnow() @pytest.mark.asyncio async def test_refresh_tokens_failure(garmin_auth_service): credentials = GarminCredentials( garmin_username="test@example.com", garmin_password_plaintext="invalid_password", access_token="old_access_token", access_token_secret="old_access_token_secret", token_expiration_date=datetime.utcnow() - timedelta(minutes=1) ) with patch('backend.src.services.garmin_auth_service.garth') as mock_garth: mock_garth.Client.return_value = AsyncMock() mock_garth.Client.return_value.reauthorize.side_effect = Exception("Garmin reauthorize failed") refreshed_credentials = await garmin_auth_service.refresh_tokens(credentials) assert refreshed_credentials is None