Skip to main content
Glama
test_client.py10.4 kB
"""Tests for ServiceNow API client.""" import json from unittest.mock import MagicMock import httpx import pytest from servicenow_mcp.client import ServiceNowClient from servicenow_mcp.exceptions import ( ServiceNowAPIError, ServiceNowAuthenticationError, ServiceNowNotFoundError, ServiceNowRateLimitError, ) class TestServiceNowClient: """Test ServiceNow client functionality.""" @pytest.mark.asyncio async def test_client_initialization(self, mock_config): """Test client initialization.""" client = ServiceNowClient(mock_config.servicenow) assert client.base_url == "https://test.service-now.com/api/now" assert client.config == mock_config.servicenow assert client._client is None @pytest.mark.asyncio async def test_context_manager(self, mock_config): """Test async context manager.""" client = ServiceNowClient(mock_config.servicenow) async with client: assert client._client is not None assert isinstance(client._client, httpx.AsyncClient) assert client._client is None @pytest.mark.asyncio async def test_get_record(self, mock_client, sample_incident): """Test getting a single record.""" # Mock the response mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = {"result": sample_incident} mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response result = await mock_client.get_record("incident", "1234567890abcdef") assert result == sample_incident mock_client._client.request.assert_called_once_with( method="GET", url="https://test.service-now.com/api/now/table/incident/1234567890abcdef", params={"sysparm_display_value": "false"}, json=None, ) @pytest.mark.asyncio async def test_query_records(self, mock_client, sample_incident): """Test querying records.""" # Mock the response mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = {"result": [sample_incident]} mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response result = await mock_client.query_records( "incident", query="active=true", fields=["number", "short_description"], limit=10, ) assert result == [sample_incident] mock_client._client.request.assert_called_once_with( method="GET", url="https://test.service-now.com/api/now/table/incident", params={ "sysparm_limit": 10, "sysparm_offset": 0, "sysparm_display_value": "false", "sysparm_query": "active=true", "sysparm_fields": "number,short_description", }, json=None, ) @pytest.mark.asyncio async def test_create_record(self, mock_client, sample_incident): """Test creating a record.""" # Mock the response mock_response = MagicMock() mock_response.status_code = 201 mock_response.json.return_value = {"result": sample_incident} mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response data = { "short_description": "Test incident", "description": "This is a test incident", } result = await mock_client.create_record("incident", data) assert result == sample_incident mock_client._client.request.assert_called_once_with( method="POST", url="https://test.service-now.com/api/now/table/incident", params={"sysparm_display_value": "false"}, json=data, ) @pytest.mark.asyncio async def test_update_record(self, mock_client, sample_incident): """Test updating a record.""" # Mock the response mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = {"result": sample_incident} mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response data = {"state": "2"} result = await mock_client.update_record("incident", "1234567890abcdef", data) assert result == sample_incident mock_client._client.request.assert_called_once_with( method="PATCH", url="https://test.service-now.com/api/now/table/incident/1234567890abcdef", params={"sysparm_display_value": "false"}, json=data, ) @pytest.mark.asyncio async def test_delete_record(self, mock_client): """Test deleting a record.""" # Mock the response mock_response = MagicMock() mock_response.status_code = 204 mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response result = await mock_client.delete_record("incident", "1234567890abcdef") assert result is True mock_client._client.request.assert_called_once_with( method="DELETE", url="https://test.service-now.com/api/now/table/incident/1234567890abcdef", params=None, json=None, ) @pytest.mark.asyncio async def test_authentication_error(self, mock_client): """Test authentication error handling.""" # Mock 401 response mock_response = MagicMock() mock_response.status_code = 401 mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response with pytest.raises(ServiceNowAuthenticationError): await mock_client.get_record("incident", "123") @pytest.mark.asyncio async def test_not_found_error(self, mock_client): """Test not found error handling.""" # Mock 404 response mock_response = MagicMock() mock_response.status_code = 404 mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response with pytest.raises(ServiceNowNotFoundError): await mock_client.get_record("incident", "123") @pytest.mark.asyncio async def test_rate_limit_error(self, mock_client): """Test rate limit error handling.""" # Mock 429 response mock_response = MagicMock() mock_response.status_code = 429 mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response with pytest.raises(ServiceNowRateLimitError): await mock_client.get_record("incident", "123") @pytest.mark.asyncio async def test_rate_limit_retry(self, mock_client): """Test rate limit retry logic.""" # First call returns 429, second call succeeds mock_response_429 = MagicMock() mock_response_429.status_code = 429 mock_response_200 = MagicMock() mock_response_200.status_code = 200 mock_response_200.json.return_value = {"result": {"success": True}} mock_response_200.raise_for_status = MagicMock() # Mock HTTPStatusError for 429 http_error = httpx.HTTPStatusError( "429 Too Many Requests", request=MagicMock(), response=mock_response_429 ) # First call raises HTTPStatusError, second succeeds mock_client._client.request.side_effect = [http_error, mock_response_200] # Override max_retries for faster test mock_client.config.max_retries = 1 result = await mock_client.get_record("incident", "123") assert result == {"success": True} assert mock_client._client.request.call_count == 2 @pytest.mark.asyncio async def test_json_decode_error(self, mock_client): """Test JSON decode error handling.""" # Mock response with invalid JSON mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.side_effect = json.JSONDecodeError("Invalid", "", 0) mock_response.text = "Invalid JSON" mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response with pytest.raises(ServiceNowAPIError, match="Invalid JSON response"): await mock_client.get_record("incident", "123") @pytest.mark.asyncio async def test_specialized_methods(self, mock_client, sample_incident, sample_user): """Test specialized methods for common tables.""" # Mock incident response mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = {"result": sample_incident} mock_response.raise_for_status = MagicMock() mock_client._client.request.return_value = mock_response # Test get_incident result = await mock_client.get_incident("123") assert result == sample_incident # Test query_incidents mock_response.json.return_value = {"result": [sample_incident]} result = await mock_client.query_incidents(query="active=true") assert result == [sample_incident] # Test create_incident mock_response.json.return_value = {"result": sample_incident} result = await mock_client.create_incident({"short_description": "Test"}) assert result == sample_incident # Test update_incident mock_response.json.return_value = {"result": sample_incident} result = await mock_client.update_incident("123", {"state": "2"}) assert result == sample_incident # Test user methods mock_response.json.return_value = {"result": sample_user} result = await mock_client.get_user("user123") assert result == sample_user # Verify correct tables were used calls = mock_client._client.request.call_args_list assert "table/incident/123" in str(calls[0]) assert "table/incident" in str(calls[1]) assert "table/incident" in str(calls[2]) assert "table/incident/123" in str(calls[3]) assert "table/sys_user/user123" in str(calls[4])

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/LokiMCPUniverse/servicenow-mcp-server'

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