Skip to main content
Glama
test_integration.py18.5 kB
from __future__ import annotations import os import sys import time from pathlib import Path from typing import TYPE_CHECKING from unittest.mock import AsyncMock, MagicMock, mock_open, patch import pytest # Add the project root directory to Python path sys.path.append(str(Path(__file__).parent.parent.parent)) from client.auth import AuthClient from client.checkin import CheckinClient from client.search import SearchClient from models.auth import TraktAuthToken if TYPE_CHECKING: from models.types import ( CheckinResponse, CommentResponse, DeviceCodeResponse, SearchResult, ShowResponse, TokenResponse, TraktRating, TrendingWrapper, ) @pytest.mark.asyncio async def test_auth_flow_integration() -> None: device_code_response: DeviceCodeResponse = { "device_code": "device_code_123", "user_code": "USER123", "verification_url": "https://trakt.tv/activate", "expires_in": 600, "interval": 5, } auth_token_response: TokenResponse = { "access_token": "access_token_123", "refresh_token": "refresh_token_123", "expires_in": 7200, "created_at": int(time.time()), "scope": "public", "token_type": "bearer", } with ( patch("httpx.AsyncClient") as mock_client, patch("builtins.open", mock_open()), patch("json.dumps", return_value="{}"), patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), patch("server.auth.tools.active_auth_flow", {}), patch("os.path.exists", return_value=True), ): device_code_mock = MagicMock() device_code_mock.json.return_value = device_code_response device_code_mock.raise_for_status = MagicMock() auth_token_mock = MagicMock() auth_token_mock.json.return_value = auth_token_response auth_token_mock.raise_for_status = MagicMock() mock_instance = mock_client.return_value.__aenter__.return_value mock_instance.aclose = AsyncMock() mock_instance.get = AsyncMock() mock_instance.post = AsyncMock(side_effect=[device_code_mock, auth_token_mock]) # Also configure the direct return value (for non-context-manager usage) mock_client.return_value.aclose = AsyncMock() mock_client.return_value.get = AsyncMock() mock_client.return_value.post = AsyncMock( side_effect=[device_code_mock, auth_token_mock] ) from server.auth.tools import start_device_auth start_auth_result = await start_device_auth() assert "USER123" in start_auth_result assert "https://trakt.tv/activate" in start_auth_result from server.auth.tools import active_auth_flow assert "device_code" in active_auth_flow assert active_auth_flow["device_code"] == "device_code_123" from server.auth.tools import check_auth_status check_auth_result = await check_auth_status() assert "Authentication Successful" in check_auth_result with patch("json.load", return_value=auth_token_response): client = AuthClient() assert client.is_authenticated() is True assert client.auth_token is not None assert client.auth_token.access_token == "access_token_123" @pytest.mark.asyncio async def test_search_and_checkin_integration() -> None: # Mock search results search_results: list[SearchResult] = [ { "type": "show", "score": 100.0, "show": { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, "overview": "A high school chemistry teacher diagnosed with inoperable lung cancer.", }, } ] # Mock checkin response checkin_response: CheckinResponse = { "id": 123456, "watched_at": "2023-05-10T20:30:00Z", "sharing": {"facebook": False, "twitter": False, "tumblr": False}, "show": { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, }, "episode": { "season": 1, "number": 1, "title": "Pilot", "ids": {"trakt": 1, "slug": "pilot"}, }, } # Set up mock responses for the focused clients with ( patch("httpx.AsyncClient") as mock_client, patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Mock for search request search_mock = MagicMock() search_mock.json.return_value = search_results search_mock.raise_for_status = MagicMock() # Mock for checkin request checkin_mock = MagicMock() checkin_mock.json.return_value = checkin_response checkin_mock.raise_for_status = MagicMock() # Configure the mock to return different responses for each call mock_instance = mock_client.return_value.__aenter__.return_value mock_instance.aclose = AsyncMock() mock_instance.get = AsyncMock(return_value=search_mock) mock_instance.post = AsyncMock(return_value=checkin_mock) # Also configure the direct return value (for non-context-manager usage) mock_client.return_value.aclose = AsyncMock() mock_client.return_value.get = AsyncMock(return_value=search_mock) mock_client.return_value.post = AsyncMock(return_value=checkin_mock) # Create clients with mock auth tokens search_client = SearchClient() checkin_client = CheckinClient() checkin_client.auth_token = TraktAuthToken( access_token="test_token", refresh_token="test_refresh", expires_in=7200, created_at=int(time.time()), scope="public", token_type="bearer", ) with ( patch("server.search.tools.SearchClient", return_value=search_client), patch("server.checkin.tools.CheckinClient", return_value=checkin_client), ): from server.search.tools import search_shows search_result = await search_shows(query="Breaking Bad") # Verify the search results contain the expected show assert "Breaking Bad (2008)" in search_result assert "ID: 1" in search_result from server.checkin.tools import checkin_to_show checkin_result = await checkin_to_show(season=1, episode=1, show_id="1") # Verify the checkin result contains the expected information assert "Successfully Checked In" in checkin_result assert "Breaking Bad" in checkin_result assert "S01E01" in checkin_result @pytest.mark.asyncio async def test_trending_shows_integration() -> None: # Mock trending shows response trending_shows: list[TrendingWrapper] = [ { "watchers": 100, "show": { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, "overview": "A high school chemistry teacher diagnosed with inoperable lung cancer.", }, }, { "watchers": 80, "show": { "title": "Stranger Things", "year": 2016, "ids": {"trakt": 2, "slug": "stranger-things"}, "overview": "When a young boy disappears, his mother and friends must confront terrifying forces.", }, }, ] # Set up mock responses for the focused clients with ( patch("httpx.AsyncClient") as mock_client, patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Configure the mock mock_instance = mock_client.return_value.__aenter__.return_value # Mock for trending shows request trending_mock = MagicMock() trending_mock.json.return_value = trending_shows trending_mock.raise_for_status = MagicMock() mock_instance.get.return_value = trending_mock with patch("server.shows.resources.ShowsClient") as mock_trending_client: mock_client_instance = mock_trending_client.return_value mock_client_instance.get_trending_shows = AsyncMock( return_value=trending_shows ) from server.shows.resources import get_trending_shows resource_result = await get_trending_shows() # Verify the resource result contains the expected shows assert "# Trending Shows on Trakt" in resource_result assert "Breaking Bad (2008)" in resource_result assert "100 watchers" in resource_result assert "Stranger Things (2016)" in resource_result assert "80 watchers" in resource_result with patch("server.shows.tools.TrendingShowsClient") as mock_trending_client: mock_client_instance = mock_trending_client.return_value mock_client_instance.get_trending_shows = AsyncMock( return_value=trending_shows[:1] ) from server.shows.tools import fetch_trending_shows tool_result = await fetch_trending_shows(limit=1) # Verify the tool result contains the expected shows assert "# Trending Shows on Trakt" in tool_result assert "Breaking Bad (2008)" in tool_result # Note: Even though we set limit=1, our mock always returns the same data # In a real scenario, the limit would be passed to the API @pytest.mark.asyncio async def test_show_ratings_integration() -> None: # Mock show data show_data: ShowResponse = { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, } # Mock ratings data ratings_data: TraktRating = { "rating": 9.0, "votes": 1000, "distribution": { "10": 500, "9": 300, "8": 100, "7": 50, "6": 20, "5": 15, "4": 10, "3": 3, "2": 1, "1": 1, }, } # Set up mock responses for the focused clients with ( patch("httpx.AsyncClient") as mock_client, patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Configure the mock mock_instance = mock_client.return_value.__aenter__.return_value # Mock for show request show_mock = MagicMock() show_mock.json.return_value = show_data show_mock.raise_for_status = MagicMock() # Mock for ratings request ratings_mock = MagicMock() ratings_mock.json.return_value = ratings_data ratings_mock.raise_for_status = MagicMock() # Set up the sequence of responses mock_instance.get.side_effect = [ show_mock, ratings_mock, show_mock, ratings_mock, ] with patch("server.shows.resources.ShowsClient") as mock_details_client: mock_client_instance = mock_details_client.return_value mock_client_instance.get_show = AsyncMock(return_value=show_data) mock_client_instance.get_show_ratings = AsyncMock(return_value=ratings_data) from server.shows.resources import get_show_ratings resource_result = await get_show_ratings(show_id="1") # Verify the resource result contains the expected ratings assert "# Ratings for Breaking Bad" in resource_result assert "**Average Rating:** 9.00/10" in resource_result assert "from 1000 votes" in resource_result with patch("server.shows.tools.ShowDetailsClient") as mock_details_client: mock_client_instance = mock_details_client.return_value mock_client_instance.get_show = AsyncMock(return_value=show_data) mock_client_instance.get_show_ratings = AsyncMock(return_value=ratings_data) from server.shows.tools import fetch_show_ratings tool_result = await fetch_show_ratings(show_id="1") # Verify the tool result contains the expected ratings assert "# Ratings for Breaking Bad" in tool_result assert "**Average Rating:** 9.00/10" in tool_result assert "from 1000 votes" in tool_result @pytest.mark.asyncio async def test_error_handling_integration() -> None: # Set up mock responses for the focused clients that will trigger errors with ( patch("httpx.AsyncClient") as mock_client, patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Configure the mock to raise an exception mock_instance = mock_client.return_value.__aenter__.return_value mock_instance.get.side_effect = Exception("API error") # Test error handling in a resource function directly with patch("server.shows.resources.ShowsClient") as mock_details_client: mock_client_instance = mock_details_client.return_value mock_client_instance.get_show = AsyncMock( side_effect=Exception("API error") ) from server.shows.resources import get_show_ratings from utils.api.errors import InternalError # Should raise an InternalError for unexpected exceptions with pytest.raises(InternalError) as exc_info: await get_show_ratings(show_id="1") assert "unexpected error occurred" in str(exc_info.value).lower() # Test error handling in a tool function directly with patch("server.shows.tools.ShowDetailsClient") as mock_details_client: mock_client_instance = mock_details_client.return_value mock_client_instance.get_show = AsyncMock( side_effect=Exception("API error") ) from server.shows.tools import fetch_show_ratings # Should raise an InternalError for unexpected exceptions with pytest.raises(InternalError) as exc_info: await fetch_show_ratings(show_id="1") assert "unexpected error occurred" in str(exc_info.value).lower() @pytest.mark.asyncio async def test_token_refresh_integration() -> None: # Since refresh_token method doesn't exist, we'll test authentication status instead with ( patch("httpx.AsyncClient") as mock_client, patch("builtins.open", mock_open()), patch("json.dumps", return_value="{}"), patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Configure the mock for API request mock_instance = mock_client.return_value.__aenter__.return_value # Mock for API request api_mock = MagicMock() show_response: ShowResponse = { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, } api_mock.json.return_value = show_response api_mock.raise_for_status = MagicMock() mock_instance.get.return_value = api_mock # Create a client with a valid token client = AuthClient() client.auth_token = TraktAuthToken( access_token="valid_access_token", refresh_token="valid_refresh_token", expires_in=7200, created_at=int(time.time()), # Current time scope="public", token_type="bearer", ) with patch("server.auth.resources.AuthClient", return_value=client): # Check authentication status from server.auth.resources import get_auth_status result = await get_auth_status() # Verify the result indicates authentication assert "You are authenticated with Trakt" in result @pytest.mark.asyncio async def test_comments_integration() -> None: # Mock comments data sample_comments: list[CommentResponse] = [ { "id": 123, "comment": "This show is amazing!", "spoiler": False, "review": False, "likes": 10, "replies": 2, "created_at": "2023-01-15T20:30:00Z", "user": { "username": "testuser", "private": False, "ids": {"slug": "testuser"}, }, } ] sample_show: ShowResponse = { "title": "Breaking Bad", "year": 2008, "ids": {"trakt": 1, "slug": "breaking-bad"}, } # Set up mock responses for the focused clients with ( patch("httpx.AsyncClient") as mock_client, patch.dict( os.environ, {"TRAKT_CLIENT_ID": "test_id", "TRAKT_CLIENT_SECRET": "test_secret"}, ), ): # Configure the mock mock_instance = mock_client.return_value.__aenter__.return_value # Mock for show request show_mock = MagicMock() show_mock.json.return_value = sample_show show_mock.raise_for_status = MagicMock() # Mock for comments request comments_mock = MagicMock() comments_mock.json.return_value = sample_comments comments_mock.raise_for_status = MagicMock() # Set up the response for comments request mock_instance.get.return_value = comments_mock # Call the function directly with patch("server.comments.tools.ShowCommentsClient") as mock_comments_client: mock_client_instance = mock_comments_client.return_value mock_client_instance.get_show_comments = AsyncMock( return_value=sample_comments ) from server.comments.tools import fetch_show_comments result = await fetch_show_comments(show_id="1") # Verify the result contains the expected comment assert "# Comments for Show ID: 1" in result assert "testuser" in result assert "This show is amazing!" in result assert "Likes: 10 | Replies: 2" in result

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wwiens/trakt_mcpserver'

If you have feedback or need assistance with the MCP directory API, please join our Discord server