Skip to main content
Glama
test_server.py20.5 kB
import unittest from unittest.mock import patch, Mock, AsyncMock, mock_open import os import tempfile import asyncio from .server import mcp_http_request, fetch_content_and_write_to_file from .request import Response, RequestError, ArgumentError def async_test(test_func): """Decorator to make async test functions work with unittest""" def wrapper(*args, **kwargs): loop = asyncio.new_event_loop() try: return loop.run_until_complete(test_func(*args, **kwargs)) finally: loop.close() return wrapper class TestMcpHttpRequest(unittest.TestCase): """Test mcp_http_request function""" @patch('mcp_server_requests.server.http_request') def test_successful_request(self, mock_http_request): """Test successful HTTP request""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "application/json")], '{"success": true}' ) mock_http_request.return_value = mock_response result = mcp_http_request("GET", "https://example.com") self.assertIn('{"success": true}', result) mock_http_request.assert_called_once_with("GET", "https://example.com", query=None, headers={}, data=None, json_=None) @patch('mcp_server_requests.server.http_request') def test_request_with_custom_headers(self, mock_http_request): """Test request with custom headers""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/plain")], "content" ) mock_http_request.return_value = mock_response custom_headers = {"Authorization": "Bearer token", "Custom": "value"} result = mcp_http_request("GET", "https://example.com", headers=custom_headers) mock_http_request.assert_called_once() call_headers = mock_http_request.call_args[1]['headers'] self.assertEqual(call_headers["Authorization"], "Bearer token") self.assertEqual(call_headers["Custom"], "value") @patch('mcp_server_requests.server.http_request') def test_request_with_user_agent(self, mock_http_request): """Test request with custom user agent""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/plain")], "content" ) mock_http_request.return_value = mock_response result = mcp_http_request( "GET", "https://example.com", user_agent="Test-Agent/1.0", force_user_agnet=True ) mock_http_request.assert_called_once() call_headers = mock_http_request.call_args[1]['headers'] self.assertEqual(call_headers["User-Agent"], "Test-Agent/1.0") @patch('mcp_server_requests.server.http_request') def test_request_with_existing_user_agent_header(self, mock_http_request): """Test request with existing User-Agent header""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/plain")], "content" ) mock_http_request.return_value = mock_response headers = {"User-Agent": "Existing-Agent/1.0"} result = mcp_http_request("GET", "https://example.com", headers=headers) mock_http_request.assert_called_once() call_headers = mock_http_request.call_args[1]['headers'] self.assertEqual(call_headers["User-Agent"], "Existing-Agent/1.0") @patch('mcp_server_requests.server.http_request') def test_request_with_query_and_data(self, mock_http_request): """Test request with query parameters and data""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/plain")], "success" ) mock_http_request.return_value = mock_response query_params = {"param1": "value1", "param2": 42} data = "test data" result = mcp_http_request( "POST", "https://example.com", query=query_params, data=data ) mock_http_request.assert_called_once_with( "POST", "https://example.com", query=query_params, headers={}, data=data, json_=None ) @patch('mcp_server_requests.server.http_request') def test_request_with_json(self, mock_http_request): """Test request with JSON data""" mock_response = Response( "https://example.com", "HTTP/1.1", 201, "Created", [("Content-Type", "application/json")], '{"id": 123}' ) mock_http_request.return_value = mock_response json_data = {"name": "test", "value": 123} result = mcp_http_request( "POST", "https://example.com", json=json_data ) mock_http_request.assert_called_once_with( "POST", "https://example.com", query=None, headers={}, data=None, json_=json_data ) @patch('mcp_server_requests.server.http_request') def test_request_error_handling(self, mock_http_request): """Test request error handling""" mock_http_request.side_effect = RequestError("Connection failed", "Network timeout") result = mcp_http_request("GET", "https://example.com") self.assertIn("encountered an internal error when making a request", result) self.assertIn("Connection failed", result) @patch('mcp_server_requests.server.http_request') def test_argument_error_handling(self, mock_http_request): """Test argument error handling""" mock_http_request.side_effect = ArgumentError("Invalid URL", "URL is required") result = mcp_http_request("GET", "") self.assertIn("found an error while checking parameters", result) self.assertIn("Invalid URL", result) @patch('mcp_server_requests.server.http_request') def test_general_exception_handling(self, mock_http_request): """Test general exception handling""" mock_http_request.side_effect = Exception("Unexpected error") result = mcp_http_request("GET", "https://example.com") self.assertIn("An unexpected error occurred", result) @patch('mcp_server_requests.server.http_request') def test_format_status_and_headers_options(self, mock_http_request): """Test format_status and format_headers options""" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/plain"), ("Cache-Control", "no-cache")], "content" ) mock_http_request.return_value = mock_response # Test with format_status=False, format_headers=False result = mcp_http_request( "GET", "https://example.com", format_status=False, format_headers=False ) self.assertEqual(result, "content") # Test with format_status=True, format_headers=True result = mcp_http_request( "GET", "https://example.com", format_status=True, format_headers=True ) self.assertIn("HTTP/1.1 200 OK", result) self.assertIn("Content-Type: text/plain", result) self.assertIn("Cache-Control: no-cache", result) self.assertIn("content", result) @patch('mcp_server_requests.server.http_request') def test_return_content_options(self, mock_http_request): """Test return_content options""" html_content = "<html><body><h1>Test</h1></body></html>" mock_response = Response( "https://example.com", "HTTP/1.1", 200, "OK", [("Content-Type", "text/html")], html_content ) mock_http_request.return_value = mock_response # Test raw format result = mcp_http_request("GET", "https://example.com", return_content="raw", format_status=False, format_headers=False) self.assertEqual(result, html_content) # Test markdown format result = mcp_http_request("GET", "https://example.com", return_content="markdown", format_status=False, format_headers=False) self.assertNotIn("<html>", result) self.assertIn("Test", result) class TestFetchContentAndWriteToFile(unittest.TestCase): """Test fetch_content_and_write_to_file function""" def setUp(self): """Set up test environment""" self.temp_dir = tempfile.mkdtemp() self.test_file_path = os.path.join(self.temp_dir, "test_file.txt") self.test_url = "https://example.com/content" self.test_content = "Test content for file" def tearDown(self): """Clean up test environment""" import shutil shutil.rmtree(self.temp_dir, ignore_errors=True) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_successful_fetch_and_write(self, mock_mcp_request): """Test successful content fetch and file write""" mock_mcp_request.return_value = self.test_content # Mock Context with minimal implementation mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) result = await fetch_content_and_write_to_file( self.test_url, self.test_file_path, "raw", mock_ctx ) self.assertIn("successfully written to", result) self.assertIn(str(len(self.test_content)), result) self.assertIn(self.test_file_path, result) # Verify file was created with correct content with open(self.test_file_path, 'r', encoding='utf-8') as f: content = f.read() self.assertEqual(content, self.test_content) mock_mcp_request.assert_called_once_with( "GET", self.test_url, return_content="raw", user_agent="mcp-server-requests", force_user_agnet=False, format_status=False, format_headers=False ) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_fetch_and_write_with_workspace_root(self, mock_mcp_request): """Test fetch and write with workspace root""" mock_mcp_request.return_value = self.test_content # Mock Context with workspace root mock_root = Mock() mock_root.uri.scheme = "file" mock_root.uri.path = self.temp_dir mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[mock_root]) relative_path = "subdir/test_file.txt" expected_full_path = os.path.join(self.temp_dir, relative_path.replace('/', os.sep)) result = await fetch_content_and_write_to_file( self.test_url, relative_path, "markdown", mock_ctx, use_workspace_root=True ) self.assertIn("successfully written to", result) self.assertIn(expected_full_path, result) # Verify file was created with open(expected_full_path, 'r', encoding='utf-8') as f: content = f.read() self.assertEqual(content, self.test_content) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_no_workspace_root_error(self, mock_mcp_request): """Test error when no workspace root is available""" mock_mcp_request.return_value = self.test_content mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) result = await fetch_content_and_write_to_file( self.test_url, "relative/path.txt", "raw", mock_ctx, use_workspace_root=True ) self.assertIn("Error: No workspace root available", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_multiple_workspace_roots_error(self, mock_mcp_request): """Test error when multiple workspace roots are found""" mock_mcp_request.return_value = self.test_content mock_root1 = Mock() mock_root1.uri.scheme = "file" mock_root2 = Mock() mock_root2.uri.scheme = "file" mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[mock_root1, mock_root2]) result = await fetch_content_and_write_to_file( self.test_url, "relative/path.txt", "raw", mock_ctx, use_workspace_root=True ) self.assertIn("Error: Multiple workspace roots found", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_non_file_uri_scheme_error(self, mock_mcp_request): """Test error when workspace root is not file:// URI""" mock_mcp_request.return_value = self.test_content mock_root = Mock() mock_root.uri.scheme = "http" mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[mock_root]) result = await fetch_content_and_write_to_file( self.test_url, "relative/path.txt", "raw", mock_ctx, use_workspace_root=True ) self.assertIn("Error: Workspace root is not a file:// URI", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_external_file_access_denied(self, mock_mcp_request): """Test error when external file access is denied""" mock_mcp_request.return_value = self.test_content mock_root = Mock() mock_root.uri.scheme = "file" mock_root.uri.path = self.temp_dir mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[mock_root]) # Try to access a path outside the workspace root external_path = os.path.join(os.path.dirname(self.temp_dir), "external.txt") result = await fetch_content_and_write_to_file( self.test_url, external_path, "raw", mock_ctx, use_workspace_root=True, allow_external_file_access=True ) self.assertIn("Error: Access denied", result) self.assertIn("outside workspace root", result) @async_test async def test_absolute_path_required_error(self): """Test error when absolute path is required but not provided""" mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) result = await fetch_content_and_write_to_file( self.test_url, "relative/path.txt", # Not absolute "raw", mock_ctx, use_workspace_root=False ) self.assertIn("Error: Path must be absolute", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_protected_paths_windows(self, mock_mcp_request): """Test protection of Windows system paths""" mock_mcp_request.return_value = self.test_content # Mock both os.name and os.path.join for Windows behavior with patch('mcp_server_requests.server.os.name', 'nt'), \ patch('os.path.join', side_effect=lambda *args: '\\'.join(args)): mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) protected_path = r"C:\Windows\test.txt" result = await fetch_content_and_write_to_file( self.test_url, protected_path, "raw", mock_ctx, use_workspace_root=False ) self.assertIn("Error: Do not allow writing to protected paths", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_protected_paths_unix(self, mock_mcp_request): """Test protection of Unix/Linux system paths""" mock_mcp_request.return_value = self.test_content # Mock os.name for Unix behavior with patch('mcp_server_requests.server.os.name', 'posix'): mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) protected_path = "/etc/test.txt" result = await fetch_content_and_write_to_file( self.test_url, protected_path, "raw", mock_ctx, use_workspace_root=False ) self.assertIn("Error: Do not allow writing to protected paths", result) @patch('mcp_server_requests.server.mcp_http_request') @patch('os.makedirs') @async_test async def test_directory_creation_error(self, mock_makedirs, mock_mcp_request): """Test error when directory creation fails""" mock_mcp_request.return_value = self.test_content mock_makedirs.side_effect = OSError("Permission denied") mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) nested_path = os.path.join(self.temp_dir, "nested", "subdir", "file.txt") result = await fetch_content_and_write_to_file( self.test_url, nested_path, "raw", mock_ctx, use_workspace_root=False ) self.assertIn("Error: Unable to create directory", result) self.assertIn("Permission denied", result) @patch('mcp_server_requests.server.mcp_http_request') @patch('builtins.open', new_callable=mock_open) @async_test async def test_file_write_error(self, mock_file, mock_mcp_request): """Test error when file write fails""" mock_mcp_request.return_value = self.test_content mock_file.side_effect = OSError("Disk full") mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) result = await fetch_content_and_write_to_file( self.test_url, self.test_file_path, "raw", mock_ctx, use_workspace_root=False ) self.assertIn("Error: Unable to write to file", result) self.assertIn("Disk full", result) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_custom_user_agent(self, mock_mcp_request): """Test custom user agent parameter""" mock_mcp_request.return_value = self.test_content mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) await fetch_content_and_write_to_file( self.test_url, self.test_file_path, "raw", mock_ctx, user_agent="Custom-Agent/1.0", force_user_agent=True ) mock_mcp_request.assert_called_once_with( "GET", self.test_url, return_content="raw", user_agent="Custom-Agent/1.0", force_user_agnet=True, format_status=False, format_headers=False ) @patch('mcp_server_requests.server.mcp_http_request') @async_test async def test_different_content_formats(self, mock_mcp_request): """Test different content processing formats""" test_html = "<html><body><h1>Test</h1></body></html>" for content_format in ["raw", "basic_clean", "strict_clean", "markdown"]: with self.subTest(format=content_format): mock_mcp_request.return_value = f"Processed as {content_format}: {test_html}" mock_ctx = Mock() mock_ctx.list_roots = AsyncMock(return_value=[]) file_path = os.path.join(self.temp_dir, f"test_{content_format}.txt") result = await fetch_content_and_write_to_file( self.test_url, file_path, content_format, mock_ctx, use_workspace_root=False ) self.assertIn("successfully written to", result) # Verify the call parameters mock_mcp_request.assert_called_with( "GET", self.test_url, return_content=content_format, user_agent="mcp-server-requests", force_user_agnet=False, format_status=False, format_headers=False ) if __name__ == '__main__': unittest.main()

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/coucya/mcp-server-requests'

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