We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/echelon-ai-labs/servicenow-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""
Tests for the change management tools.
"""
import unittest
from unittest.mock import MagicMock, patch
import requests
from servicenow_mcp.auth.auth_manager import AuthManager
from servicenow_mcp.tools.change_tools import (
create_change_request,
list_change_requests,
)
from servicenow_mcp.utils.config import AuthConfig, AuthType, BasicAuthConfig, ServerConfig
class TestChangeTools(unittest.TestCase):
"""Tests for the change management tools."""
def setUp(self):
"""Set up test fixtures."""
self.auth_config = AuthConfig(
type=AuthType.BASIC,
basic=BasicAuthConfig(username="test_user", password="test_password"),
)
self.server_config = ServerConfig(
instance_url="https://test.service-now.com",
auth=self.auth_config,
)
self.auth_manager = AuthManager(self.auth_config)
@patch("servicenow_mcp.tools.change_tools.requests.get")
def test_list_change_requests_success(self, mock_get):
"""Test listing change requests successfully."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {
"result": [
{
"sys_id": "change123",
"number": "CHG0010001",
"short_description": "Test Change",
"type": "normal",
"state": "open",
},
{
"sys_id": "change456",
"number": "CHG0010002",
"short_description": "Another Test Change",
"type": "emergency",
"state": "in progress",
},
]
}
mock_response.raise_for_status = MagicMock()
mock_get.return_value = mock_response
# Call the function
params = {
"limit": 10,
"timeframe": "upcoming",
}
result = list_change_requests(self.auth_manager, self.server_config, params)
# Verify the result
self.assertTrue(result["success"])
self.assertEqual(len(result["change_requests"]), 2)
self.assertEqual(result["count"], 2)
self.assertEqual(result["total"], 2)
self.assertEqual(result["change_requests"][0]["sys_id"], "change123")
self.assertEqual(result["change_requests"][1]["sys_id"], "change456")
@patch("servicenow_mcp.tools.change_tools.requests.get")
def test_list_change_requests_empty_result(self, mock_get):
"""Test listing change requests with empty result."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {"result": []}
mock_response.raise_for_status = MagicMock()
mock_get.return_value = mock_response
# Call the function
params = {
"limit": 10,
"timeframe": "upcoming",
}
result = list_change_requests(self.auth_manager, self.server_config, params)
# Verify the result
self.assertTrue(result["success"])
self.assertEqual(len(result["change_requests"]), 0)
self.assertEqual(result["count"], 0)
self.assertEqual(result["total"], 0)
@patch("servicenow_mcp.tools.change_tools.requests.get")
def test_list_change_requests_missing_result(self, mock_get):
"""Test listing change requests with missing result key."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {} # No "result" key
mock_response.raise_for_status = MagicMock()
mock_get.return_value = mock_response
# Call the function
params = {
"limit": 10,
"timeframe": "upcoming",
}
result = list_change_requests(self.auth_manager, self.server_config, params)
# Verify the result
self.assertTrue(result["success"])
self.assertEqual(len(result["change_requests"]), 0)
self.assertEqual(result["count"], 0)
self.assertEqual(result["total"], 0)
@patch("servicenow_mcp.tools.change_tools.requests.get")
def test_list_change_requests_error(self, mock_get):
"""Test listing change requests with error."""
# Mock the response
mock_get.side_effect = requests.exceptions.RequestException("Test error")
# Call the function
params = {
"limit": 10,
"timeframe": "upcoming",
}
result = list_change_requests(self.auth_manager, self.server_config, params)
# Verify the result
self.assertFalse(result["success"])
self.assertIn("Error listing change requests", result["message"])
@patch("servicenow_mcp.tools.change_tools.requests.get")
def test_list_change_requests_with_filters(self, mock_get):
"""Test listing change requests with filters."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {
"result": [
{
"sys_id": "change123",
"number": "CHG0010001",
"short_description": "Test Change",
"type": "normal",
"state": "open",
}
]
}
mock_response.raise_for_status = MagicMock()
mock_get.return_value = mock_response
# Call the function with filters
params = {
"limit": 10,
"state": "open",
"type": "normal",
"category": "Hardware",
"assignment_group": "IT Support",
"timeframe": "upcoming",
"query": "short_description=Test",
}
result = list_change_requests(self.auth_manager, self.server_config, params)
# Verify the result
self.assertTrue(result["success"])
self.assertEqual(len(result["change_requests"]), 1)
# Verify that the correct query parameters were passed to the request
args, kwargs = mock_get.call_args
self.assertIn("params", kwargs)
self.assertIn("sysparm_query", kwargs["params"])
query = kwargs["params"]["sysparm_query"]
# Check that all filters are in the query
self.assertIn("state=open", query)
self.assertIn("type=normal", query)
self.assertIn("category=Hardware", query)
self.assertIn("assignment_group=IT Support", query)
self.assertIn("short_description=Test", query)
# The timeframe filter adds a date comparison, which is harder to test exactly
@patch("servicenow_mcp.tools.change_tools.requests.post")
def test_create_change_request_with_swapped_parameters(self, mock_post):
"""Test creating a change request with swapped parameters (server_config used as auth_manager)."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {
"result": {
"sys_id": "change123",
"number": "CHG0010001",
"short_description": "Test Change",
"type": "normal",
}
}
mock_response.raise_for_status = MagicMock()
mock_post.return_value = mock_response
# Create a server_config with a get_headers method to simulate what might happen in Claude Desktop
server_config_with_headers = MagicMock()
server_config_with_headers.instance_url = "https://test.service-now.com"
server_config_with_headers.get_headers.return_value = {"Authorization": "Basic dGVzdF91c2VyOnRlc3RfcGFzc3dvcmQ="}
# Call the function with swapped parameters (server_config as auth_manager)
params = {
"short_description": "Test Change",
"type": "normal",
"risk": "low",
"impact": "medium",
}
result = create_change_request(server_config_with_headers, self.auth_manager, params)
# Verify the result
self.assertTrue(result["success"])
self.assertEqual(result["change_request"]["sys_id"], "change123")
self.assertEqual(result["change_request"]["number"], "CHG0010001")
@patch("servicenow_mcp.tools.change_tools.requests.post")
def test_create_change_request_with_serverconfig_no_get_headers(self, mock_post):
"""Test creating a change request with ServerConfig object that doesn't have get_headers method."""
# This test simulates the exact error we're seeing in Claude Desktop
# Create params for the change request
params = {
"short_description": "Test Change",
"type": "normal",
"risk": "low",
"impact": "medium",
}
# Create a real ServerConfig object (which doesn't have get_headers method)
# and a mock AuthManager object (which doesn't have instance_url)
real_server_config = ServerConfig(
instance_url="https://test.service-now.com",
auth=self.auth_config,
)
mock_auth_manager = MagicMock()
# Explicitly remove get_headers method to simulate the error
if hasattr(mock_auth_manager, 'get_headers'):
delattr(mock_auth_manager, 'get_headers')
# Call the function with parameters that will cause the error
result = create_change_request(real_server_config, mock_auth_manager, params)
# The function should detect the issue and return an error message
self.assertFalse(result["success"])
self.assertIn("Cannot find get_headers method", result["message"])
# Verify that the post method was never called
mock_post.assert_not_called()
@patch("servicenow_mcp.tools.change_tools.requests.post")
def test_create_change_request_with_swapped_parameters_real(self, mock_post):
"""Test creating a change request with swapped parameters (auth_manager and server_config)."""
# Mock the response
mock_response = MagicMock()
mock_response.json.return_value = {
"result": {
"sys_id": "change123",
"number": "CHG0010001",
"short_description": "Test Change",
"type": "normal",
}
}
mock_response.raise_for_status = MagicMock()
mock_post.return_value = mock_response
# Create params for the change request
params = {
"short_description": "Test Change",
"type": "normal",
"risk": "low",
"impact": "medium",
}
# Call the function with swapped parameters (server_config as first parameter, auth_manager as second)
result = create_change_request(self.server_config, self.auth_manager, params)
# The function should still work correctly
self.assertTrue(result["success"])
self.assertEqual(result["change_request"]["sys_id"], "change123")
self.assertEqual(result["change_request"]["number"], "CHG0010001")
if __name__ == "__main__":
unittest.main()