Skip to main content
Glama
drewster99

xcode-mcp-server (drewster99)

by drewster99
test_minimal.py8.66 kB
#!/usr/bin/env python3 """ Minimal tests that verify MCP functionality without triggering Xcode UI operations. These tests focus on validating the MCP server's logic and error handling. """ import os import time import shutil from pathlib import Path from test_runner import XcodeMCPTestRunner, TestHelpers class MinimalTests(XcodeMCPTestRunner): """Minimal tests focused on MCP server logic validation.""" def test_version(self): """Test version command works and returns valid format.""" result = self.run_mcp_tool("version") self.assert_success(result) # Validate format version = result["result"] assert "Xcode MCP Server version" in version assert len(version.split(".")) >= 2 # Should have version number like x.y.z def test_path_validation_logic(self): """Test path validation without opening Xcode.""" print("\nTesting path validation logic...") # Test 1: Empty path result = self.run_mcp_tool("get_project_hierarchy", project_path="") self.assert_failure(result) assert "cannot be empty" in result.get("error", "") print("✓ Empty path rejected") # Test 2: Invalid extension result = self.run_mcp_tool("get_project_hierarchy", project_path=str(self.working_dir / "test.txt")) self.assert_failure(result) assert ".xcodeproj' or '.xcworkspace'" in result.get("error", "") print("✓ Invalid extension rejected") # Test 3: Path outside allowed folders result = self.run_mcp_tool("get_project_hierarchy", project_path="/private/tmp/Test.xcodeproj") self.assert_failure(result) assert "not allowed" in result.get("error", "") print("✓ Path outside allowed folders rejected") # Test 4: Valid path format but doesn't exist (within allowed folder) fake_path = self.working_dir / "NonExistent.xcodeproj" result = self.run_mcp_tool("get_project_hierarchy", project_path=str(fake_path)) self.assert_failure(result) assert "does not exist" in result.get("error", "") print("✓ Non-existent project rejected") def test_project_discovery_fallback(self): """Test project discovery with filesystem fallback when mdfind fails.""" print("\nTesting project discovery...") # Create simple xcodeproj directories (don't need to be valid) test_proj1 = self.working_dir / "TestProj1.xcodeproj" test_proj2 = self.working_dir / "TestProj2.xcworkspace" test_proj1.mkdir(exist_ok=True) test_proj2.mkdir(exist_ok=True) # Create marker files so they look like projects (test_proj1 / "project.pbxproj").touch() (test_proj2 / "contents.xcworkspacedata").touch() # Search for projects - even if mdfind returns nothing, # we should have a fallback that finds them result = self.run_mcp_tool("get_xcode_projects", search_path=str(self.working_dir)) # Current implementation uses mdfind which might not find them # This is actually a bug we should fix if result["success"]: projects = result["result"].strip() if projects: print(f"✓ Found projects: {projects[:100]}") else: print("⚠ No projects found by mdfind (expected, shows need for fallback)") # Clean up shutil.rmtree(test_proj1) shutil.rmtree(test_proj2) def test_error_handling(self): """Test that errors are handled gracefully.""" print("\nTesting error handling...") # Test with special characters in path special_path = self.working_dir / "Test's \"Special\" Project.xcodeproj" result = self.run_mcp_tool("get_project_hierarchy", project_path=str(special_path)) self.assert_failure(result) # Should handle the path gracefully even with quotes assert "error" in result or not result["success"] print("✓ Special characters handled") # Test with very long path long_name = "A" * 200 long_path = self.working_dir / f"{long_name}.xcodeproj" result = self.run_mcp_tool("get_project_hierarchy", project_path=str(long_path)) self.assert_failure(result) print("✓ Long path handled") def test_parameter_validation(self): """Test that all parameter validations work correctly.""" print("\nTesting parameter validation...") # Create a dummy valid project path valid_project = self.working_dir / "Valid.xcodeproj" valid_project.mkdir(exist_ok=True) (valid_project / "project.pbxproj").touch() # Test build_project parameters result = self.run_mcp_tool("build_project", project_path=str(valid_project), include_warnings="not_a_boolean") # Invalid type self.assert_failure(result) assert "boolean" in result.get("error", "").lower() print("✓ Invalid boolean parameter rejected") # Test run_project parameters result = self.run_mcp_tool("run_project", project_path=str(valid_project), wait_seconds=-5) # Negative wait time self.assert_failure(result) assert "non-negative" in result.get("error", "") or "negative" in result.get("error", "") print("✓ Negative wait_seconds rejected") # Clean up shutil.rmtree(valid_project) def test_helper_functions(self): """Test that helper functions work correctly.""" print("\nTesting helper functions...") # Test escape_applescript_string import xcode_mcp_server.__main__ as mcp_server # Test escaping quotes escaped = mcp_server.escape_applescript_string('Test "quoted" string') assert '\\"' in escaped print("✓ Quotes escaped correctly") # Test escaping backslashes escaped = mcp_server.escape_applescript_string('Test\\path') assert '\\\\' in escaped print("✓ Backslashes escaped correctly") # Test path normalization test_path = self.working_dir / "Test.xcodeproj" test_path.mkdir(exist_ok=True) normalized = mcp_server.validate_and_normalize_project_path( str(test_path), "Testing") assert os.path.isabs(normalized) print("✓ Path normalization works") # Clean up shutil.rmtree(test_path) def test_security_boundaries(self): """Test security boundaries are properly enforced.""" print("\nTesting security boundaries...") # Test that we can't access parent directories parent_path = str(self.working_dir.parent / "Test.xcodeproj") result = self.run_mcp_tool("get_project_hierarchy", project_path=parent_path) self.assert_failure(result) assert "not allowed" in result.get("error", "") print("✓ Parent directory access blocked") # Test that we can't use .. in paths traversal_path = str(self.working_dir / ".." / "Test.xcodeproj") result = self.run_mcp_tool("get_project_hierarchy", project_path=traversal_path) self.assert_failure(result) # Should either be blocked or normalized away print("✓ Path traversal handled") def run_minimal_tests(): """Run all minimal tests.""" print("\n" + "=" * 60) print("RUNNING MINIMAL TESTS (NO XCODE UI)") print("=" * 60) tests = MinimalTests() tests.setup() try: # Run each test tests.run_test(tests.test_version, "Version Command") tests.run_test(tests.test_path_validation_logic, "Path Validation Logic") tests.run_test(tests.test_project_discovery_fallback, "Project Discovery") tests.run_test(tests.test_error_handling, "Error Handling") tests.run_test(tests.test_parameter_validation, "Parameter Validation") tests.run_test(tests.test_helper_functions, "Helper Functions") tests.run_test(tests.test_security_boundaries, "Security Boundaries") # Print summary return tests.print_summary() finally: tests.teardown() if __name__ == "__main__": import sys success = run_minimal_tests() sys.exit(0 if success else 1)

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/drewster99/xcode-mcp-server'

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