from datetime import datetime, timedelta from unittest.mock import AsyncMock, patch import pytest from fastapi.testclient import TestClient from src.main import app from src.schemas import GarminCredentials @pytest.fixture def mock_garmin_auth_service(): with patch("src.api.garmin_auth.GarminAuthService") as MockGarminAuthService: service_instance = MockGarminAuthService.return_value yield service_instance @pytest.fixture def mock_central_db_service(): with patch("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 with TestClient(app=app) as client: response = 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 with TestClient(app=app) as client: response = 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 ) with TestClient(app=app) as client: response = 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 ) with TestClient(app=app) as client: response = 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 ) with TestClient(app=app) as client: response = 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." }