| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| """ |
| Tools Manager Client-Server Integration Tests |
| |
| Tests the tools functionality through actual MCP client-server communication |
| Assumes the server is already running and configured properly |
| """ |
| |
| import os |
| import sys |
| |
| import pytest |
| |
| # Add project root to path |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) |
| |
| from test.test_config_loader import ( |
| create_test_client, |
| get_test_config, |
| test_server_connectivity, |
| ) |
| |
| |
| class TestToolsClientServer: |
| """Test tools functionality through client-server communication""" |
| |
| @pytest.fixture |
| def test_config(self): |
| """Get test configuration""" |
| return get_test_config() |
| |
| @pytest.fixture |
| async def client(self, test_config): |
| """Create test client""" |
| return create_test_client() |
| |
| @pytest.fixture(scope="class", autouse=True) |
| async def check_server_connectivity(self): |
| """Check server connectivity before running tests""" |
| is_connected = await test_server_connectivity() |
| if not is_connected: |
| pytest.skip("Server is not running or not accessible") |
| |
| @pytest.mark.asyncio |
| async def test_list_tools_via_client(self, client, test_config): |
| """Test listing tools through client-server communication""" |
| expected_tools = test_config.get_expected_tools() |
| |
| async def test_callback(client_instance): |
| tools = await client_instance.list_all_tools() |
| |
| # Verify we got tools back |
| assert len(tools) > 0, "No tools returned from server" |
| |
| # Verify expected tools are present |
| tool_names = [tool.name for tool in tools] |
| for expected_tool in expected_tools: |
| assert expected_tool in tool_names, f"Expected tool '{expected_tool}' not found" |
| |
| return tools |
| |
| await client.connect_and_run(test_callback) |
| |
| @pytest.mark.asyncio |
| async def test_call_query_child_via_client(self, client, test_config): |
| """Test calling the exact query child through the client.""" |
| sample_queries = test_config.get_sample_queries() |
| |
| async def test_callback(client_instance): |
| result = await client_instance.execute_sql( |
| sample_queries[0], |
| max_rows=100, |
| ) |
| |
| assert result.get("mode") in {"result", "error"} |
| |
| return result |
| |
| await client.connect_and_run(test_callback) |
| |
| @pytest.mark.asyncio |
| async def test_call_database_list_child_via_client(self, client, test_config): |
| """Test calling the exact database-list child through the client.""" |
| async def test_callback(client_instance): |
| result = await client_instance.get_database_list() |
| |
| assert result.get("mode") in {"result", "error"} |
| |
| return result |
| |
| await client.connect_and_run(test_callback) |
| |
| @pytest.mark.asyncio |
| async def test_call_table_context_schema_via_client(self, client, test_config): |
| """Test calling the schema section of table context.""" |
| test_tables = test_config.get_test_tables() |
| |
| async def test_callback(client_instance): |
| result = await client_instance.get_table_schema( |
| test_tables[0], |
| "information_schema", |
| ) |
| |
| assert result.get("mode") in {"result", "error"} |
| return result |
| |
| await client.connect_and_run(test_callback) |
| |
| @pytest.mark.asyncio |
| async def test_tool_error_handling_via_client(self, client, test_config): |
| """Test tool error handling through client""" |
| async def test_callback(client_instance): |
| result = await client_instance.execute_sql( |
| "INVALID SQL SYNTAX HERE" |
| ) |
| |
| assert result.get("mode") in {"result", "error"} |
| return result |
| |
| await client.connect_and_run(test_callback) |
| |
| @pytest.mark.asyncio |
| async def test_tool_with_auth_token_via_client(self, client, test_config): |
| """Test tool calls with authentication token""" |
| if not test_config.is_security_tests_enabled(): |
| pytest.skip("Security tests are disabled") |
| |
| async def test_callback(client_instance): |
| result = await client_instance.get_database_list() |
| |
| assert result.get("mode") in {"result", "error"} |
| return result |
| |
| await client.connect_and_run(test_callback) |