Skip to main content
Glama
test_nyt_client.py8.49 kB
"""Tests for nyt_client.py HTTP client functionality.""" import asyncio from datetime import datetime, timedelta from unittest.mock import AsyncMock, Mock, patch import httpx import pytest from nytimes_mcp.nyt_client import NytClient, filter_params class TestFilterParams: """Tests for filter_params function.""" def test_filters_none_values(self): """Test that None values are filtered out.""" params = {"key1": "value1", "key2": None, "key3": "value3"} result = filter_params(params) assert result == {"key1": "value1", "key3": "value3"} assert "key2" not in result def test_filters_empty_strings(self): """Test that empty strings are filtered out.""" params = {"key1": "value1", "key2": "", "key3": "value3"} result = filter_params(params) assert result == {"key1": "value1", "key3": "value3"} assert "key2" not in result def test_filters_zero_values(self): """Test that 0 values are filtered out.""" params = {"key1": "value1", "key2": 0, "key3": 42} result = filter_params(params) assert result == {"key1": "value1", "key3": 42} assert "key2" not in result def test_keeps_valid_values(self): """Test that valid values are kept.""" params = { "string": "value", "number": 42, "negative": -1, "float": 3.14, "bool_true": True, "list": [1, 2, 3], } result = filter_params(params) # All should be kept assert "string" in result assert "number" in result assert "negative" in result assert "float" in result assert "bool_true" in result assert "list" in result def test_filters_false_value(self): """Test that False is filtered (since False == 0).""" params = {"bool_false": False, "other": "value"} result = filter_params(params) # False should be filtered out since False == 0 in Python assert "bool_false" not in result assert "other" in result def test_empty_dict(self): """Test with empty dictionary.""" params = {} result = filter_params(params) assert result == {} def test_all_filtered(self): """Test when all values are filtered.""" params = {"key1": None, "key2": "", "key3": 0} result = filter_params(params) assert result == {} class TestNytClientInit: """Tests for NytClient initialization.""" def test_client_initialization(self): """Test that client initializes correctly with settings.""" client = NytClient() assert isinstance(client.client, httpx.AsyncClient) assert isinstance(client.api_key, str) assert len(client.api_key) > 0 assert client.last_call == datetime.min class TestNytClientMakeRequest: """Tests for NytClient.make_nyt_request method.""" async def test_make_request_success( self, mock_httpx_response, sample_article_search_response ): """Test successful API request.""" client = NytClient() # Mock the httpx client's get method mock_response = mock_httpx_response(sample_article_search_response) client.client.get = AsyncMock(return_value=mock_response) result = await client.make_nyt_request( "search/v2/articlesearch.json", {"q": "test"} ) assert result == sample_article_search_response client.client.get.assert_called_once() # Verify the call arguments call_args = client.client.get.call_args assert "https://api.nytimes.com/svc/search/v2/articlesearch.json" in str( call_args ) async def test_adds_api_key_to_params(self, mock_httpx_response): """Test that API key is added to request params.""" client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) await client.make_nyt_request("test/endpoint.json", {"param": "value"}) # Check that api-key was added to params call_args = client.client.get.call_args params = call_args.kwargs["params"] assert "api-key" in params assert isinstance(params["api-key"], str) assert len(params["api-key"]) > 0 async def test_filters_params(self, mock_httpx_response): """Test that params are filtered before request.""" client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) await client.make_nyt_request( "test/endpoint.json", {"valid": "value", "none_value": None, "empty": "", "zero": 0}, ) # Check that filtered params were used call_args = client.client.get.call_args params = call_args.kwargs["params"] assert "valid" in params assert "none_value" not in params assert "empty" not in params assert "zero" not in params async def test_constructs_correct_url(self, mock_httpx_response): """Test that URL is constructed correctly.""" client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) await client.make_nyt_request("books/v3/lists/overview.json", {}) call_args = client.client.get.call_args url = call_args.args[0] assert url == "https://api.nytimes.com/svc/books/v3/lists/overview.json" async def test_raises_on_http_error(self): """Test that HTTP errors are raised.""" client = NytClient() # Mock a failed response mock_response = Mock() mock_response.raise_for_status.side_effect = httpx.HTTPStatusError( "Not Found", request=Mock(), response=Mock() ) client.client.get = AsyncMock(return_value=mock_response) with pytest.raises(httpx.HTTPStatusError): await client.make_nyt_request("test/endpoint.json", {}) async def test_updates_last_call_timestamp(self, mock_httpx_response): """Test that last_call timestamp is updated.""" client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) initial_last_call = client.last_call await client.make_nyt_request("test/endpoint.json", {}) assert client.last_call > initial_last_call assert client.last_call <= datetime.now() @patch("nytimes_mcp.nyt_client.asyncio.sleep") async def test_rate_limiting(self, mock_sleep, mock_httpx_response): """Test that rate limiting works correctly.""" from nytimes_mcp.config import settings client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) # Make first request await client.make_nyt_request("test/endpoint.json", {}) assert mock_sleep.call_count == 0 # Make second request immediately - should trigger rate limiting await client.make_nyt_request("test/endpoint.json", {}) # Should have called sleep with approximately the rate limit seconds assert mock_sleep.call_count == 1 sleep_duration = mock_sleep.call_args[0][0] assert sleep_duration > 0 assert sleep_duration <= settings.nyt_rate_limit_seconds @patch("nytimes_mcp.nyt_client.asyncio.sleep") async def test_no_rate_limiting_after_delay(self, mock_sleep, mock_httpx_response): """Test that no rate limiting occurs after sufficient delay.""" from nytimes_mcp.config import settings client = NytClient() mock_response = mock_httpx_response({"status": "OK"}) client.client.get = AsyncMock(return_value=mock_response) # Make first request await client.make_nyt_request("test/endpoint.json", {}) # Simulate time passing by setting last_call to past client.last_call = datetime.now() - timedelta( seconds=settings.nyt_rate_limit_seconds + 1 ) # Make second request - should not trigger rate limiting await client.make_nyt_request("test/endpoint.json", {}) assert mock_sleep.call_count == 0

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/jeffmm/nytimes-mcp'

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