Skip to main content
Glama
test_validation.pyโ€ข13.8 kB
#!/usr/bin/env python3 """ Validation tests for Fast Apply MCP server. Tests code validation, configuration validation, and input validation logic. """ import os import sys import tempfile import unittest import pytest from fastapply.main import FastApplyConnector, call_tool, validate_code_quality # Add the parent directory to sys.path to import main sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) class TestCodeValidation(unittest.TestCase): """Test code validation functionality.""" def test_valid_python_code(self): """Test validation of valid Python code.""" code = """ def hello(): return "world" print(hello()) """ result = validate_code_quality("test.py", code) self.assertFalse(result["has_errors"]) self.assertEqual(len(result["errors"]), 0) def test_python_syntax_error(self): """Test validation of Python code with syntax errors.""" code = """ def hello( return "world" # Missing closing parenthesis """ result = validate_code_quality("test.py", code) # In test environment without linters, expect basic validation structure self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) def test_python_undefined_variable(self): """Test validation of Python code with undefined variables.""" code = """ def test(): print(undefined_variable) # This should be detected """ result = validate_code_quality("test.py", code) # In test environment without linters, expect basic validation structure self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) def test_python_dangerous_functions(self): """Test validation of Python code with dangerous functions.""" code = """ def test(): result = eval(user_input) # This should generate a warning return result """ # In test environment without linters, expect basic validation structure result = validate_code_quality("test.py", code) self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) def test_python_unused_imports(self): """Test validation of Python code with unused imports.""" code = """ import unused_module import math def test(): return math.sqrt(4) """ # In test environment without linters, expect basic validation structure result = validate_code_quality("test.py", code) self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) def test_javascript_undefined_variable(self): """Test validation of JavaScript code with undefined variables.""" code = """ function test() { console.log(undefinedVar); # This should be detected } """ # In test environment without linters, expect basic validation structure result = validate_code_quality("test.js", code) self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) def test_javascript_var_usage(self): """Test validation of JavaScript code using var instead of let/const.""" code = """ function test() { var x = 10; # This should generate a suggestion return x; } """ # In test environment without linters, expect basic validation structure result = validate_code_quality("test.js", code) self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) class TestConfigurationValidation(unittest.TestCase): """Test configuration parameter validation.""" def test_timeout_validation(self): """Test timeout parameter validation.""" # Valid timeouts FastApplyConnector(timeout=30.0) FastApplyConnector(timeout=1.0) FastApplyConnector(timeout=300.0) # Invalid timeouts with self.assertRaises(ValueError): FastApplyConnector(timeout=-1.0) with self.assertRaises(ValueError): FastApplyConnector(timeout=301.0) def test_max_tokens_validation(self): """Test max_tokens parameter validation.""" # Valid max_tokens FastApplyConnector(max_tokens=1000) FastApplyConnector(max_tokens=32000) # Invalid max_tokens with self.assertRaises(ValueError): FastApplyConnector(max_tokens=0) with self.assertRaises(ValueError): FastApplyConnector(max_tokens=-1) with self.assertRaises(ValueError): FastApplyConnector(max_tokens=32001) def test_temperature_validation(self): """Test temperature parameter validation.""" # Valid temperatures FastApplyConnector(temperature=0.0) FastApplyConnector(temperature=1.0) FastApplyConnector(temperature=2.0) # Invalid temperatures with self.assertRaises(ValueError): FastApplyConnector(temperature=-0.1) with self.assertRaises(ValueError): FastApplyConnector(temperature=2.1) def test_invalid_combination_validation(self): """Test validation of parameter combinations.""" # Test that invalid combinations are rejected with self.assertRaises(ValueError): FastApplyConnector(timeout=-1.0, max_tokens=1000) with self.assertRaises(ValueError): FastApplyConnector(timeout=30.0, max_tokens=0) class TestInputValidation(unittest.TestCase): """Test input validation for various operations.""" def setUp(self): """Set up test environment.""" self.test_dir = tempfile.mkdtemp() self.connector = FastApplyConnector() def tearDown(self): """Clean up test environment.""" if hasattr(self, "test_dir"): import shutil shutil.rmtree(self.test_dir) def test_apply_edit_input_validation(self): """Test input validation for apply_edit operation.""" # Test with missing required parameters using actual FastApplyConnector connector = FastApplyConnector() # These should raise TypeError due to missing required parameters with self.assertRaises(TypeError): connector.apply_edit(original_code=None, code_edit=None, instruction="", file_path="") def test_read_file_input_validation(self): """Test input validation for read_multiple_files operation.""" # Test with empty paths list with pytest.raises(ValueError, match="paths parameter is required and must be non-empty"): # Use asyncio.run to test async function import asyncio asyncio.run(call_tool("read_multiple_files", {"paths": []})) # Test with None paths with pytest.raises(ValueError, match="paths parameter is required and must be non-empty"): import asyncio asyncio.run(call_tool("read_multiple_files", {"paths": None})) def test_write_file_input_validation(self): """Test input validation for edit_file operation.""" # Test with empty path with pytest.raises(ValueError, match="target_file parameter is required"): import asyncio asyncio.run(call_tool("edit_file", {"path": "", "code_edit": "test", "instruction": "test"})) # Test with None content with pytest.raises(ValueError, match="code_edit parameter is required"): import asyncio asyncio.run(call_tool("edit_file", {"path": "test.txt", "code_edit": None, "instruction": "test"})) def test_search_files_input_validation(self): """Test input validation for search_files operation.""" # Test with empty pattern with pytest.raises(ValueError, match="pattern parameter is required"): import asyncio asyncio.run(call_tool("search_files", {"pattern": "", "file_mask": "*.py"})) # Test with None pattern with pytest.raises(ValueError, match="pattern parameter is required"): import asyncio asyncio.run(call_tool("search_files", {"pattern": None, "file_mask": "*.py"})) def test_read_multiple_files_input_validation(self): """Test input validation for read_multiple_files operation.""" # Test with None paths (already tested above) # Test with empty paths list (already tested above) # Test with invalid path in list - should handle gracefully try: import asyncio result = asyncio.run(call_tool("read_multiple_files", {"paths": ["valid.py", "../etc/passwd"]})) # Should return partial results with error for invalid path self.assertIn("text", result[0]) response_text = result[0]["text"] self.assertIn("valid.py", response_text) self.assertIn("Error - File not found", response_text) except ValueError: # Also acceptable if it raises an exception pass class TestResponseParsingValidation(unittest.TestCase): """Test response parsing validation.""" def setUp(self): """Set up test connector.""" self.connector = FastApplyConnector() def tearDown(self): """Clean up test environment.""" if hasattr(self, "test_dir"): import shutil shutil.rmtree(self.test_dir) def test_empty_response_validation(self): """Test validation of empty responses.""" with self.assertRaises(ValueError) as cm: self.connector._parse_fast_apply_response("") self.assertIn("empty", str(cm.exception)) def test_malformed_xml_validation(self): """Test validation of malformed XML responses.""" # Unclosed tag malformed = "<updated-code>print('hello')" result = self.connector._parse_fast_apply_response(malformed) # Should fall back to processed content self.assertIsInstance(result, str) def test_response_size_validation(self): """Test validation of response size limits.""" # Import the constant that's actually used from fastapply.main import MAX_RESPONSE_SIZE # Create a very large response large_response = "A" * (MAX_RESPONSE_SIZE + 1000) result = self.connector._parse_fast_apply_response(large_response) # Should be truncated to safe size self.assertLessEqual(len(result), MAX_RESPONSE_SIZE) def test_multiple_code_blocks_validation(self): """Test validation of responses with multiple code blocks.""" response = """ <updated-code>print("first")</updated-code> Some text in between <updated-code>print("second")</updated-code> """ result = self.connector._parse_fast_apply_response(response) self.assertIn('print("first")', result) self.assertIn('print("second")', result) class TestQualityMetricsValidation(unittest.TestCase): """Test quality metrics validation.""" def test_quality_score_validation(self): """Test validation of quality scores.""" test_cases = [ ("good.py", "def hello():\n return 'Hello, World!'\n"), ("bad.py", "def x():\n y=1+2\n return y\n"), # Poor formatting ( "complex.py", "def complex_function(a,b,c,d,e,f):\n if a and b and c and d and e and f:\n return True\n return False\n", ), ] for filename, content in test_cases: with self.subTest(filename=filename): result = validate_code_quality(filename, content) self.assertIsInstance(result, dict) self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) # Check that errors and warnings are lists self.assertIsInstance(result["errors"], list) self.assertIsInstance(result["warnings"], list) self.assertIsInstance(result["suggestions"], list) # Check that has_errors is boolean self.assertIsInstance(result["has_errors"], bool) def test_validation_categories_validation(self): """Test validation of different validation categories.""" code = """ def test(): eval(user_input) # Dangerous print(undefined_variable) # Undefined import unused_module # Unused """ result = validate_code_quality("test.py", code) # Should have all categories self.assertIn("has_errors", result) self.assertIn("errors", result) self.assertIn("warnings", result) self.assertIn("suggestions", result) # Categories should be lists self.assertIsInstance(result["errors"], list) self.assertIsInstance(result["warnings"], list) self.assertIsInstance(result["suggestions"], list) # Check that has_errors is boolean self.assertIsInstance(result["has_errors"], bool)

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/betmoar/FastApply-MCP'

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