from unittest.mock import MagicMock, patch import pytest from fastapi.testclient import TestClient from main import app client = TestClient(app, raise_server_exceptions=False) @pytest.fixture def mock_sync(): with patch("main.GarminSync") as mock: yield mock @pytest.fixture def mock_engine(): with patch("main.RecommendationEngine") as mock: yield mock def test_health(): response = client.get("/health") assert response.status_code == 200 assert response.json() == {"status": "ok"} def test_get_activities_success(mock_sync): mock_instance = mock_sync.return_value mock_instance.load_local_activities.return_value = [{"activityId": 1}] response = client.get("/activities") assert response.status_code == 200 assert response.json() == [{"activityId": 1}] def test_get_activities_error(mock_sync): mock_instance = mock_sync.return_value mock_instance.load_local_activities.side_effect = Exception("Load failed") response = client.get("/activities") assert response.status_code == 500 assert "INTERNAL_SERVER_ERROR" in response.json()["error"] def test_get_recommendation(mock_sync, mock_engine): mock_sync_instance = mock_sync.return_value mock_sync_instance.load_local_activities.return_value = [] mock_engine_instance = mock_engine.return_value mock_engine_instance.get_recommendation.return_value = "Great job!" response = client.get("/recommendation") assert response.status_code == 200 assert response.json() == {"recommendation": "Great job!"} def test_auth_status_unauthenticated(monkeypatch): monkeypatch.setenv("GARMIN_EMAIL", "") response = client.get("/auth/status") assert response.json()["authenticated"] is False def test_auth_status_failure(monkeypatch): monkeypatch.setenv("GARMIN_EMAIL", "test@test.com") with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "FAILURE" response = client.get("/auth/status") assert response.json()["authenticated"] is False assert response.json()["message"] == "Login failed" def test_auth_status_success(monkeypatch, mock_sync): monkeypatch.setenv("GARMIN_EMAIL", "test@test.com") monkeypatch.setenv("GARMIN_PASSWORD", "pass") with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "SUCCESS" response = client.get("/auth/status") assert response.json()["authenticated"] is True def test_auth_status_mfa_required(monkeypatch): monkeypatch.setenv("GARMIN_EMAIL", "test@test.com") with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "MFA_REQUIRED" response = client.get("/auth/status") assert response.json()["status"] == "MFA_REQUIRED" def test_login_success(mock_sync): with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "SUCCESS" with patch("builtins.open", MagicMock()): response = client.post("/auth/login", json={"email": "a", "password": "b"}) assert response.status_code == 200 assert response.json()["status"] == "SUCCESS" def test_login_mfa_required(): with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "MFA_REQUIRED" response = client.post("/auth/login", json={"email": "a", "password": "b"}) assert response.json()["status"] == "MFA_REQUIRED" def test_login_missing_data(monkeypatch): monkeypatch.setenv("GARMIN_EMAIL", "") monkeypatch.setenv("GARMIN_PASSWORD", "") response = client.post("/auth/login", json={}) assert response.status_code == 400 def test_login_invalid_creds(): with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "FAILURE" response = client.post("/auth/login", json={"email": "a", "password": "b"}) assert response.status_code == 401 def test_trigger_sync_success(mock_sync): with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "SUCCESS" mock_sync.return_value.sync_activities.return_value = 5 response = client.post("/sync") assert response.status_code == 200 assert response.json()["synced_count"] == 5 def test_trigger_sync_unauthorized(): with patch("main.GarminClient") as mock_client: mock_client.return_value.login.return_value = "FAILURE" response = client.post("/sync") assert response.status_code == 401