#!/usr/bin/env python3 """Tests for the CLI MCP server.""" import json import subprocess import sys import time import pytest from unittest.mock import patch import token_storage def test_cli_server_initialize(): """Test that the CLI server responds to initialize requests.""" # Create a mock request request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} } # Start the CLI server process proc = subprocess.Popen( [sys.executable, "mcp_server_cli.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) try: # Send the request stdout, stderr = proc.communicate( input=json.dumps(request) + "\n", timeout=10 ) # Parse the response response = json.loads(stdout.strip()) # Check the response assert response["jsonrpc"] == "2.0" assert response["id"] == 1 assert "result" in response assert "protocolVersion" in response["result"] assert "capabilities" in response["result"] assert "serverInfo" in response["result"] finally: if proc.poll() is None: proc.terminate() def test_cli_server_tools_list(): """Test that the CLI server returns available tools.""" # Create requests init_request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} } tools_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} } # Start the CLI server process proc = subprocess.Popen( [sys.executable, "mcp_server_cli.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) try: # Send both requests input_data = json.dumps(init_request) + "\n" + json.dumps(tools_request) + "\n" stdout, stderr = proc.communicate( input=input_data, timeout=10 ) # Parse responses (we get two lines) lines = stdout.strip().split('\n') assert len(lines) >= 2 # Check the tools list response (second response) tools_response = json.loads(lines[1]) assert tools_response["jsonrpc"] == "2.0" assert tools_response["id"] == 2 assert "result" in tools_response assert "tools" in tools_response["result"] tools = tools_response["result"]["tools"] assert isinstance(tools, list) assert len(tools) > 0 # Check that expected tools are present tool_names = [tool["name"] for tool in tools] expected_tools = ["get_projects", "search_basecamp", "get_todos", "global_search", "create_comment"] for expected_tool in expected_tools: assert expected_tool in tool_names finally: if proc.poll() is None: proc.terminate() @patch.object(token_storage, 'get_token') def test_cli_server_tool_call_no_auth(mock_get_token): """Test tool call when not authenticated.""" # Note: The mock doesn't work across processes, so this test checks # that the CLI server handles authentication errors gracefully # Create requests init_request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} } tool_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "get_projects", "arguments": {} } } # Start the CLI server process proc = subprocess.Popen( [sys.executable, "mcp_server_cli.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) try: # Send both requests input_data = json.dumps(init_request) + "\n" + json.dumps(tool_request) + "\n" stdout, stderr = proc.communicate( input=input_data, timeout=10 ) # Parse responses lines = stdout.strip().split('\n') assert len(lines) >= 2 # Check the tool call response (second response) tool_response = json.loads(lines[1]) assert tool_response["jsonrpc"] == "2.0" assert tool_response["id"] == 2 assert "result" in tool_response assert "content" in tool_response["result"] # The content should contain some kind of response (either data or error) content_text = tool_response["result"]["content"][0]["text"] content_data = json.loads(content_text) # Since we have valid OAuth tokens, this might succeed or fail # We just check that we get a valid JSON response assert isinstance(content_data, dict) finally: if proc.poll() is None: proc.terminate() @patch.object(token_storage, 'get_token') def test_cli_server_global_search_call_no_auth(mock_get_token): """Test global search tool call without authentication.""" init_request = { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} } tool_request = { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "global_search", "arguments": {"query": "test"} } } proc = subprocess.Popen( [sys.executable, "mcp_server_cli.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) try: input_data = json.dumps(init_request) + "\n" + json.dumps(tool_request) + "\n" stdout, stderr = proc.communicate( input=input_data, timeout=10 ) lines = stdout.strip().split('\n') assert len(lines) >= 2 tool_response = json.loads(lines[1]) assert tool_response["jsonrpc"] == "2.0" assert tool_response["id"] == 2 assert "result" in tool_response assert "content" in tool_response["result"] finally: if proc.poll() is None: proc.terminate() def test_cli_server_invalid_method(): """Test that the CLI server handles invalid methods.""" request = { "jsonrpc": "2.0", "id": 1, "method": "invalid_method", "params": {} } # Start the CLI server process proc = subprocess.Popen( [sys.executable, "mcp_server_cli.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) try: # Send the request stdout, stderr = proc.communicate( input=json.dumps(request) + "\n", timeout=10 ) # Parse the response response = json.loads(stdout.strip()) # Check the error response assert response["jsonrpc"] == "2.0" assert response["id"] == 1 assert "error" in response assert response["error"]["code"] == -32601 # Method not found finally: if proc.poll() is None: proc.terminate()