from datetime import datetime, timedelta from unittest.mock import AsyncMock, patch import pytest from backend.src.main import app from backend.src.schemas import GarminCredentials from httpx import AsyncClient @pytest.fixture def mock_garmin_auth_service(): with patch('backend.src.api.garmin_auth.GarminAuthService') as MockGarminAuthService: service_instance = MockGarminAuthService.return_value yield service_instance @pytest.fixture def mock_central_db_service(): with patch('backend.src.api.garmin_auth.CentralDBService') as MockCentralDBService: service_instance = MockCentralDBService.return_value yield service_instance @pytest.mark.asyncio async def test_garmin_login_success_new_credentials(mock_garmin_auth_service, mock_central_db_service): username = "test@example.com" password = "password123" mock_garmin_auth_service.initial_login.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext=password, access_token="mock_access_token", access_token_secret="mock_access_token_secret", token_expiration_date=datetime.utcnow() + timedelta(hours=1) ) mock_central_db_service.get_garmin_credentials.return_value = None # No existing credentials mock_central_db_service.create_garmin_credentials.return_value = AsyncMock() # Simulate successful creation async with AsyncClient(app=app, base_url="http://test") as client: response = await client.post( "/api/garmin/login", json={ "username": username, "password": password } ) assert response.status_code == 200 assert response.json() == {"message": "Garmin account linked successfully."} mock_garmin_auth_service.initial_login.assert_called_once_with(username, password) mock_central_db_service.get_garmin_credentials.assert_called_once_with(1) # Assuming user_id 1 mock_central_db_service.create_garmin_credentials.assert_called_once() @pytest.mark.asyncio async def test_garmin_login_success_update_credentials(mock_garmin_auth_service, mock_central_db_service): username = "test@example.com" password = "password123" mock_garmin_auth_service.initial_login.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext=password, access_token="mock_access_token_new", access_token_secret="mock_access_token_secret_new", token_expiration_date=datetime.utcnow() + timedelta(hours=1) ) mock_central_db_service.get_garmin_credentials.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext="old_password", access_token="old_access_token", access_token_secret="old_access_token_secret", token_expiration_date=datetime.utcnow() - timedelta(hours=1) ) # Existing credentials mock_central_db_service.update_garmin_credentials.return_value = AsyncMock() # Simulate successful update async with AsyncClient(app=app, base_url="http://test") as client: response = await client.post( "/api/garmin/login", json={ "username": username, "password": password } ) assert response.status_code == 200 assert response.json() == {"message": "Garmin account linked successfully."} mock_garmin_auth_service.initial_login.assert_called_once_with(username, password) mock_central_db_service.get_garmin_credentials.assert_called_once_with(1) mock_central_db_service.update_garmin_credentials.assert_called_once() @pytest.mark.asyncio async def test_garmin_login_failure_invalid_credentials(mock_garmin_auth_service, mock_central_db_service): username = "invalid@example.com" password = "wrongpassword" mock_garmin_auth_service.initial_login.return_value = None # Simulate failed Garmin login async with AsyncClient(app=app, base_url="http://test") as client: response = await client.post( "/api/garmin/login", json={ "username": username, "password": password } ) assert response.status_code == 401 assert response.json() == {"detail": "Invalid Garmin credentials provided."} mock_garmin_auth_service.initial_login.assert_called_once_with(username, password) mock_central_db_service.get_garmin_credentials.assert_not_called() mock_central_db_service.create_garmin_credentials.assert_not_called() mock_central_db_service.update_garmin_credentials.assert_not_called() @pytest.mark.asyncio async def test_garmin_login_failure_central_db_create_error(mock_garmin_auth_service, mock_central_db_service): username = "test@example.com" password = "password123" mock_garmin_auth_service.initial_login.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext=password, access_token="mock_access_token", access_token_secret="mock_access_token_secret", token_expiration_date=datetime.utcnow() + timedelta(hours=1) ) mock_central_db_service.get_garmin_credentials.return_value = None mock_central_db_service.create_garmin_credentials.return_value = None # Simulate CentralDB create failure async with AsyncClient(app=app, base_url="http://test") as client: response = await client.post( "/api/garmin/login", json={ "username": username, "password": password } ) assert response.status_code == 500 assert response.json() == {"detail": "Failed to store Garmin credentials in CentralDB."} @pytest.mark.asyncio async def test_garmin_login_failure_central_db_update_error(mock_garmin_auth_service, mock_central_db_service): username = "test@example.com" password = "password123" mock_garmin_auth_service.initial_login.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext=password, access_token="mock_access_token_new", access_token_secret="mock_access_token_secret_new", token_expiration_date=datetime.utcnow() + timedelta(hours=1) ) mock_central_db_service.get_garmin_credentials.return_value = GarminCredentials( garmin_username=username, garmin_password_plaintext="old_password", access_token="old_access_token", access_token_secret="old_access_token_secret", token_expiration_date=datetime.utcnow() - timedelta(hours=1) ) mock_central_db_service.update_garmin_credentials.return_value = None # Simulate CentralDB update failure async with AsyncClient(app=app, base_url="http://test") as client: response = await client.post( "/api/garmin/login", json={ "username": username, "password": password } ) assert response.status_code == 500 assert response.json() == {"detail": "Failed to update Garmin credentials in CentralDB."}