Files
mcp-garmin/tests/test_server.py
2026-05-17 19:46:45 +02:00

148 lines
5.2 KiB
Python

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")