import json import os import queue import threading from datetime import date from unittest.mock import MagicMock, patch import pytest from garminconnect import GarminConnectAuthenticationError import server @pytest.fixture(autouse=True) def reset_state(): original_state = server._auth_state original_client = server._client for q in (server._mfa_input_queue, server._login_result_queue): while not q.empty(): try: q.get_nowait() except queue.Empty: break yield server._auth_state = original_state server._client = original_client for q in (server._mfa_input_queue, server._login_result_queue): while not q.empty(): try: q.get_nowait() except queue.Empty: break def test_check_auth_unauthenticated(): server._auth_state = "unauthenticated" assert server._check_auth() == "Not authenticated. Ask Claude to call authenticate() first." def test_check_auth_authenticated(): server._auth_state = "authenticated" assert server._check_auth() is None def test_authenticate_success(): env = {"GARMIN_EMAIL": "test@example.com", "GARMIN_PASSWORD": "secret"} with patch.dict("os.environ", env): with patch("server.Garmin") as mock_garmin_cls: mock_garmin_cls.return_value = MagicMock() # mock login() returns immediately, thread puts success in queue result = server.authenticate() assert result == "Authenticated successfully." assert server._auth_state == "authenticated" def test_authenticate_mfa_required(): env = {"GARMIN_EMAIL": "test@example.com", "GARMIN_PASSWORD": "secret"} with patch.dict("os.environ", env): with patch("server.Garmin") as mock_garmin_cls: mock_garmin_cls.return_value = MagicMock() with patch.object(server._login_result_queue, "get", side_effect=queue.Empty): result = server.authenticate() assert "MFA required" in result assert server._auth_state == "mfa_pending" def test_complete_mfa_success(): server._auth_state = "mfa_pending" server._login_result_queue.put(("success", None)) result = server.complete_mfa("123456") assert result == "MFA accepted. Authenticated successfully." assert server._auth_state == "authenticated" assert server._mfa_input_queue.get_nowait() == "123456" def test_complete_mfa_not_in_progress(): server._auth_state = "unauthenticated" result = server.complete_mfa("123456") assert result == "No MFA in progress. Call authenticate() first." def test_authenticate_missing_credentials(): with patch.dict("os.environ", {}, clear=False): # Ensure the env vars are absent os.environ.pop("GARMIN_EMAIL", None) os.environ.pop("GARMIN_PASSWORD", None) result = server.authenticate() assert result == "GARMIN_EMAIL and GARMIN_PASSWORD environment variables are required." def test_get_activities_unauthenticated(): server._auth_state = "unauthenticated" assert "Not authenticated" in server.get_activities() def test_get_activities_success(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activities_by_date.return_value = [ {"activityId": "111", "activityType": {"typeKey": "running"}, "distance": 5000.0}, {"activityId": "222", "activityType": {"typeKey": "cycling"}, "distance": 20000.0}, ] result = server.get_activities("2026-05-01", "2026-05-17", 20) data = json.loads(result) assert len(data) == 2 assert data[0]["activityId"] == "111" server._client.get_activities_by_date.assert_called_once_with("2026-05-01", "2026-05-17") def test_get_activities_limit(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activities_by_date.return_value = [{"activityId": str(i)} for i in range(10)] data = json.loads(server.get_activities("2026-05-01", "2026-05-17", 3)) assert len(data) == 3 def test_get_activities_defaults_to_today(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activities_by_date.return_value = [] server.get_activities() today = date.today().isoformat() server._client.get_activities_by_date.assert_called_once_with(today, today) def test_get_activities_auth_error(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activities_by_date.side_effect = GarminConnectAuthenticationError("auth") assert "Authentication error" in server.get_activities() def test_get_activity_details_success(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activity_details.return_value = {"activityId": "111", "laps": []} data = json.loads(server.get_activity_details("111")) assert data["activityId"] == "111" server._client.get_activity_details.assert_called_once_with("111") def test_get_activity_details_error(): server._auth_state = "authenticated" server._client = MagicMock() server._client.get_activity_details.side_effect = Exception("not found") assert "Error fetching activity details" in server.get_activity_details("999")