281 lines
11 KiB
Python
281 lines
11 KiB
Python
from unittest.mock import 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
|
|
|
|
@pytest.fixture
|
|
def mock_settings_manager():
|
|
with patch("main.SettingsManager") as mock:
|
|
yield mock
|
|
|
|
@pytest.fixture
|
|
def mock_workout_manager():
|
|
with patch("main.WorkoutManager") 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_success(mock_sync, mock_engine, monkeypatch):
|
|
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
|
|
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_get_recommendation_missing_key(mock_sync, mock_engine, monkeypatch):
|
|
monkeypatch.setenv("GEMINI_API_KEY", "")
|
|
response = client.get("/recommendation")
|
|
assert "not configured" in response.json()["recommendation"]
|
|
|
|
def test_settings_status():
|
|
with patch("main.env") as mock_env:
|
|
mock_env.get_status.return_value = {"configured": True}
|
|
response = client.get("/settings")
|
|
assert response.status_code == 200
|
|
assert "garmin" in response.json()
|
|
|
|
def test_settings_status_v2():
|
|
response = client.get("/settings/status")
|
|
assert response.status_code == 200
|
|
assert "garmin" in response.json()
|
|
|
|
def test_update_settings_garmin():
|
|
with patch("main.env") as mock_env:
|
|
response = client.post("/settings/garmin", json={"email": "a", "password": "b"})
|
|
assert response.status_code == 200
|
|
mock_env.set_credentials.assert_called_once()
|
|
|
|
def test_update_settings_garmin_missing():
|
|
response = client.post("/settings/garmin", json={})
|
|
assert response.status_code == 400
|
|
|
|
def test_update_settings_withings():
|
|
with patch("main.env") as mock_env:
|
|
response = client.post("/settings/withings", json={"client_id": "a", "client_secret": "b"})
|
|
assert response.status_code == 200
|
|
mock_env.set_credentials.assert_called_once()
|
|
|
|
def test_update_settings_gemini():
|
|
with patch("main.env") as mock_env:
|
|
response = client.post("/settings/gemini", json={"api_key": "a"})
|
|
assert response.status_code == 200
|
|
mock_env.set_credentials.assert_called_once()
|
|
|
|
def test_auth_status_success(monkeypatch):
|
|
monkeypatch.setenv("GARMIN_EMAIL", "test@test.com")
|
|
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_login_mfa():
|
|
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_sync_smart(mock_sync):
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
mock_sync.return_value.sync_smart.return_value = 10
|
|
response = client.post("/sync/smart")
|
|
assert response.json()["synced_count"] == 10
|
|
|
|
def test_sync_smart_fail(mock_sync):
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "FAILURE"
|
|
response = client.post("/sync/smart")
|
|
assert response.json()["success"] is False
|
|
|
|
def test_analyze_stats(mock_sync):
|
|
mock_sync.return_value.get_weekly_stats.return_value = {"labels": ["W1"]}
|
|
response = client.get("/analyze/stats")
|
|
assert response.json()["weekly"]["labels"] == ["W1"]
|
|
|
|
def test_analyze_stats_error(mock_sync):
|
|
mock_sync.return_value.get_weekly_stats.side_effect = Exception("Err")
|
|
response = client.get("/analyze/stats")
|
|
assert "weekly" in response.json()
|
|
|
|
def test_profile(mock_settings_manager):
|
|
mock_settings_manager.return_value.load_profile.return_value = {"name": "Test"}
|
|
response = client.get("/settings/profile")
|
|
assert response.json()["name"] == "Test"
|
|
|
|
response = client.post("/settings/profile", json={"name": "New"})
|
|
assert response.json()["status"] == "SUCCESS"
|
|
|
|
def test_analyze_chat(mock_engine, monkeypatch):
|
|
monkeypatch.setenv("GEMINI_API_KEY", "k")
|
|
mock_engine.return_value.chat_with_data.return_value = "Hello"
|
|
response = client.post("/analyze/chat", json={"message": "hi"})
|
|
assert response.json()["message"] == "Hello"
|
|
|
|
def test_workouts_list():
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
mock_client.return_value.get_workouts_list.return_value = []
|
|
response = client.get("/workouts")
|
|
assert response.status_code == 200
|
|
|
|
def test_workouts_chat(mock_workout_manager):
|
|
# main.py line 289 returns {"workout": workout}
|
|
mock_workout_manager.return_value.generate_workout_json.return_value = {"ok": True}
|
|
response = client.post("/workouts/chat", json={"prompt": "test"})
|
|
assert response.json()["workout"]["ok"] is True
|
|
|
|
def test_workout_constants(mock_workout_manager):
|
|
mock_workout_manager.return_value.get_constants.return_value = {"C": 1}
|
|
response = client.get("/workouts/constants")
|
|
assert response.json()["C"] == 1
|
|
|
|
def test_workout_upload(mock_workout_manager):
|
|
mock_workout_manager.return_value.validate_workout_json.return_value = []
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
mock_client.return_value.upload_workout.return_value = {"id": 1}
|
|
response = client.post("/workouts/upload", json={"name": "W"})
|
|
assert response.json()["success"] is True
|
|
|
|
def test_update_settings_withings_missing():
|
|
response = client.post("/settings/withings", json={})
|
|
assert response.status_code == 400
|
|
|
|
def test_update_settings_gemini_missing():
|
|
response = client.post("/settings/gemini", json={})
|
|
assert response.status_code == 400
|
|
|
|
def test_auth_status_not_configured(monkeypatch):
|
|
monkeypatch.setenv("GARMIN_EMAIL", "")
|
|
response = client.get("/auth/status")
|
|
assert response.json()["authenticated"] is False
|
|
|
|
def test_sync_full_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 = 100
|
|
response = client.post("/sync/full")
|
|
assert response.json()["synced_count"] == 100
|
|
|
|
def test_sync_full_fail():
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "FAILURE"
|
|
response = client.post("/sync/full")
|
|
assert response.status_code == 401
|
|
|
|
def test_sync_smart_error(mock_sync):
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
mock_sync.return_value.sync_smart.side_effect = Exception("Smart fail")
|
|
response = client.post("/sync/smart")
|
|
assert response.json()["success"] is False
|
|
|
|
def test_workouts_list_fail():
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "FAILURE"
|
|
response = client.get("/workouts")
|
|
assert response.status_code == 401
|
|
|
|
def test_workouts_chat_error(mock_workout_manager):
|
|
mock_workout_manager.return_value.generate_workout_json.side_effect = Exception("AI Fail")
|
|
response = client.post("/workouts/chat", json={"prompt": "test"})
|
|
assert "AI Fail" in response.json()["error"]
|
|
|
|
def test_dashboard_stats_error(mock_sync):
|
|
mock_sync.return_value.get_dashboard_stats.side_effect = Exception("Dash fail")
|
|
response = client.get("/analyze/dashboard")
|
|
assert "Dash fail" in response.json()["error"]
|
|
|
|
def test_workout_validate_invalid(mock_workout_manager):
|
|
mock_workout_manager.return_value.validate_workout_json.return_value = ["Error"]
|
|
response = client.post("/workouts/validate", json={})
|
|
assert response.json()["valid"] is False
|
|
|
|
def test_workout_upload_fail_validate(mock_workout_manager):
|
|
mock_workout_manager.return_value.validate_workout_json.return_value = ["Error"]
|
|
response = client.post("/workouts/upload", json={})
|
|
assert response.json()["success"] is False
|
|
|
|
def test_workout_upload_fail_login(mock_workout_manager):
|
|
mock_workout_manager.return_value.validate_workout_json.return_value = []
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "FAILURE"
|
|
response = client.post("/workouts/upload", json={})
|
|
assert response.json()["success"] is False
|
|
|
|
def test_workout_upload_exception(mock_workout_manager):
|
|
mock_workout_manager.return_value.validate_workout_json.return_value = []
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
mock_client.return_value.upload_workout.side_effect = Exception("Upload fail")
|
|
response = client.post("/workouts/upload", json={})
|
|
assert response.json()["success"] is False
|
|
|
|
def test_login_save_credentials(monkeypatch):
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "SUCCESS"
|
|
with patch("main.env") as mock_env:
|
|
response = client.post("/auth/login", json={"email": "new@test.com", "password": "new"})
|
|
assert response.status_code == 200
|
|
mock_env.set_credentials.assert_called_once()
|
|
def test_trigger_sync_endpoint_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.json()["synced_count"] == 5
|
|
|
|
def test_trigger_sync_endpoint_fail():
|
|
with patch("main.GarminClient") as mock_client:
|
|
mock_client.return_value.login.return_value = "FAILURE"
|
|
response = client.post("/sync")
|
|
assert response.status_code == 401
|
|
|
|
def test_login_credentials_missing():
|
|
response = client.post("/auth/login", json={"email": ""})
|
|
assert response.status_code == 400
|
|
|
|
def test_login_failed_error():
|
|
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
|