Skip to main content
Glama
johannhartmann

MCP Code Analysis Server

test_baseparser_interface.py15.5 kB
"""Test BaseParser interface with TypeScript and JavaScript parsers.""" import tempfile from collections.abc import Iterator from pathlib import Path import pytest from src.parser.base_parser import ElementType, ParseResult from src.parser.plugin_registry import LanguagePluginRegistry class TestBaseParserInterface: """Test that TypeScript and JavaScript parsers properly implement BaseParser interface.""" @pytest.fixture def temp_dir(self) -> Iterator[Path]: """Create temporary directory for test files.""" with tempfile.TemporaryDirectory() as temp_dir: yield Path(temp_dir) def test_typescript_baseparser_interface(self, temp_dir: Path) -> None: """Test TypeScript parser implements BaseParser interface correctly.""" plugin = LanguagePluginRegistry.get_plugin("typescript") assert plugin is not None try: parser = plugin.create_parser() # Create a complex TypeScript file ts_file = temp_dir / "complex.ts" ts_content = """ import { EventEmitter } from 'events'; import axios, { AxiosResponse } from 'axios'; interface ApiResponse<T> { data: T; status: number; message?: string; } interface User { id: number; name: string; email: string; roles: Role[]; } interface Role { id: number; name: string; permissions: string[]; } abstract class BaseService { protected apiUrl: string; constructor(apiUrl: string) { this.apiUrl = apiUrl; } abstract getData(): Promise<any>; } class UserService extends BaseService implements EventEmitter { private users: Map<number, User> = new Map(); constructor(apiUrl: string) { super(apiUrl); } async fetchUser(id: number): Promise<User | null> { try { const response: AxiosResponse<ApiResponse<User>> = await axios.get(`${this.apiUrl}/users/${id}`); const user = response.data.data; this.users.set(user.id, user); return user; } catch (error) { console.error('Failed to fetch user:', error); return null; } } async createUser(userData: Omit<User, 'id'>): Promise<User> { const response = await axios.post<ApiResponse<User>>(`${this.apiUrl}/users`, userData); const newUser = response.data.data; this.users.set(newUser.id, newUser); return newUser; } getUserById(id: number): User | undefined { return this.users.get(id); } async getData(): Promise<User[]> { return Array.from(this.users.values()); } } // Generic utility function function processApiResponse<T>(response: ApiResponse<T>): T | null { if (response.status >= 200 && response.status < 300) { return response.data; } return null; } // Arrow function with type constraints const filterUsersByRole = <T extends User>(users: T[], roleName: string): T[] => { return users.filter(user => user.roles.some(role => role.name === roleName) ); }; export { UserService, User, Role, ApiResponse }; export default processApiResponse; """ ts_file.write_text(ts_content) # Test BaseParser interface result = parser.parse_file(ts_file) # Verify ParseResult structure assert isinstance(result, ParseResult) assert result.file_path == ts_file assert result.language == "typescript" assert result.success is True assert len(result.errors) == 0 # Verify root element assert result.root_element.type == ElementType.MODULE assert result.root_element.name == "complex" # Verify imports are extracted assert len(result.imports) > 0 import_strings = list(result.imports) assert any("EventEmitter" in imp for imp in import_strings) assert any("axios" in imp for imp in import_strings) # Verify dependencies are extracted assert len(result.dependencies) > 0 assert "events" in result.dependencies assert "axios" in result.dependencies # Verify elements are extracted classes = result.find_elements(ElementType.CLASS) assert len(classes) >= 2 # BaseService, UserService class_names = [cls.name for cls in classes] assert "BaseService" in class_names assert "UserService" in class_names # Verify functions are extracted functions = result.find_elements(ElementType.FUNCTION) assert len(functions) >= 2 # processApiResponse, filterUsersByRole function_names = [func.name for func in functions] assert "processApiResponse" in function_names assert "filterUsersByRole" in function_names # Verify methods are extracted methods = result.find_elements(ElementType.METHOD) assert ( len(methods) >= 4 ) # constructor, fetchUser, createUser, getUserById, getData # Verify references are extracted assert isinstance(result.references, list) # Should have references for inheritance, imports, type usage, etc. # Verify metadata is populated user_service = next( (cls for cls in classes if cls.name == "UserService"), None ) assert user_service is not None assert "base_classes" in user_service.metadata assert "BaseService" in user_service.metadata["base_classes"] except ImportError: pytest.skip("tree-sitter-typescript not available") def test_javascript_baseparser_interface(self, temp_dir: Path) -> None: """Test JavaScript parser implements BaseParser interface correctly.""" plugin = LanguagePluginRegistry.get_plugin("javascript") assert plugin is not None try: parser = plugin.create_parser() # Create a complex JavaScript file js_file = temp_dir / "complex.js" js_content = """ const EventEmitter = require('events'); const axios = require('axios'); class BaseService { constructor(apiUrl) { this.apiUrl = apiUrl; } async getData() { throw new Error('Not implemented'); } } class UserService extends BaseService { constructor(apiUrl) { super(apiUrl); this.users = new Map(); this.eventEmitter = new EventEmitter(); } async fetchUser(id) { try { const response = await axios.get(`${this.apiUrl}/users/${id}`); const user = response.data.data; this.users.set(user.id, user); this.eventEmitter.emit('userFetched', user); return user; } catch (error) { console.error('Failed to fetch user:', error); this.eventEmitter.emit('error', error); return null; } } async createUser(userData) { const response = await axios.post(`${this.apiUrl}/users`, userData); const newUser = response.data.data; this.users.set(newUser.id, newUser); this.eventEmitter.emit('userCreated', newUser); return newUser; } getUserById(id) { return this.users.get(id); } async getData() { return Array.from(this.users.values()); } static createInstance(apiUrl) { return new UserService(apiUrl); } } // Regular function function processApiResponse(response) { if (response.status >= 200 && response.status < 300) { return response.data; } return null; } // Arrow function const filterUsersByRole = (users, roleName) => { return users.filter(user => user.roles && user.roles.some(role => role.name === roleName) ); }; // Async arrow function const fetchAllUsers = async (apiUrl) => { const service = new UserService(apiUrl); return await service.getData(); }; module.exports = { UserService, processApiResponse, filterUsersByRole, fetchAllUsers }; """ js_file.write_text(js_content) # Test BaseParser interface result = parser.parse_file(js_file) # Verify ParseResult structure assert isinstance(result, ParseResult) assert result.file_path == js_file assert result.language == "javascript" assert result.success is True assert len(result.errors) == 0 # Verify root element assert result.root_element.type == ElementType.MODULE assert result.root_element.name == "complex" # Verify imports are extracted assert len(result.imports) > 0 import_strings = list(result.imports) assert any("events" in imp for imp in import_strings) assert any("axios" in imp for imp in import_strings) # Verify elements are extracted classes = result.find_elements(ElementType.CLASS) assert len(classes) >= 2 # BaseService, UserService class_names = [cls.name for cls in classes] assert "BaseService" in class_names assert "UserService" in class_names # Verify functions are extracted functions = result.find_elements(ElementType.FUNCTION) assert ( len(functions) >= 3 ) # processApiResponse, filterUsersByRole, fetchAllUsers function_names = [func.name for func in functions] assert "processApiResponse" in function_names assert "filterUsersByRole" in function_names assert "fetchAllUsers" in function_names # Verify methods are extracted methods = result.find_elements(ElementType.METHOD) assert ( len(methods) >= 5 ) # constructor, fetchUser, createUser, getUserById, getData # Verify references are extracted assert isinstance(result.references, list) # Verify metadata is populated user_service = next( (cls for cls in classes if cls.name == "UserService"), None ) assert user_service is not None assert "base_classes" in user_service.metadata assert "BaseService" in user_service.metadata["base_classes"] except ImportError: pytest.skip("tree-sitter-javascript not available") def test_typescript_entity_extraction_integration(self, temp_dir: Path) -> None: """Test that TypeScript parser integrates with database entity extraction.""" plugin = LanguagePluginRegistry.get_plugin("typescript") assert plugin is not None try: parser = plugin.create_parser() # Create TypeScript file with specific patterns ts_file = temp_dir / "entities.ts" ts_content = """ export interface ProductData { id: string; name: string; price: number; } export class ProductService { private products: ProductData[] = []; constructor() { this.products = []; } addProduct(product: ProductData): void { this.products.push(product); } getProduct(id: string): ProductData | undefined { return this.products.find(p => p.id === id); } } export function createProduct(name: string, price: number): ProductData { return { id: Math.random().toString(36), name, price }; } """ ts_file.write_text(ts_content) # Test entity extraction for database entities = parser.extract_entities(ts_file, file_id=1) # Verify entity structure assert isinstance(entities, dict) assert "modules" in entities assert "classes" in entities assert "functions" in entities assert "imports" in entities # Verify module entities modules = entities["modules"] assert len(modules) == 1 assert modules[0]["name"] == "entities" assert modules[0]["file_id"] == 1 # Verify class entities classes = entities["classes"] assert len(classes) == 1 assert classes[0]["name"] == "ProductService" assert classes[0]["file_id"] == 1 assert classes[0]["methods"] >= 2 # constructor, addProduct, getProduct # Verify function entities functions = entities["functions"] # Should include both standalone functions and methods function_names = [f["name"] for f in functions] assert "createProduct" in function_names assert "ProductService.addProduct" in function_names assert "ProductService.getProduct" in function_names except ImportError: pytest.skip("tree-sitter-typescript not available") def test_javascript_entity_extraction_integration(self, temp_dir: Path) -> None: """Test that JavaScript parser integrates with database entity extraction.""" plugin = LanguagePluginRegistry.get_plugin("javascript") assert plugin is not None try: parser = plugin.create_parser() # Create JavaScript file with specific patterns js_file = temp_dir / "entities.js" js_content = """ class ProductService { constructor() { this.products = []; } addProduct(product) { this.products.push(product); } getProduct(id) { return this.products.find(p => p.id === id); } static createInstance() { return new ProductService(); } } function createProduct(name, price) { return { id: Math.random().toString(36), name, price }; } const validateProduct = (product) => { return product && product.name && product.price > 0; }; module.exports = { ProductService, createProduct, validateProduct }; """ js_file.write_text(js_content) # Test entity extraction for database entities = parser.extract_entities(js_file, file_id=2) # Verify entity structure assert isinstance(entities, dict) assert "modules" in entities assert "classes" in entities assert "functions" in entities assert "imports" in entities # Verify module entities modules = entities["modules"] assert len(modules) == 1 assert modules[0]["name"] == "entities" assert modules[0]["file_id"] == 2 # Verify class entities classes = entities["classes"] assert len(classes) == 1 assert classes[0]["name"] == "ProductService" assert classes[0]["file_id"] == 2 assert classes[0]["methods"] >= 3 # constructor, addProduct, getProduct # Verify function entities functions = entities["functions"] function_names = [f["name"] for f in functions] assert "createProduct" in function_names assert "validateProduct" in function_names assert "ProductService.addProduct" in function_names assert "ProductService.getProduct" in function_names except ImportError: pytest.skip("tree-sitter-javascript not available")

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/johannhartmann/mcpcodeanalysis'

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