""" Unit tests for MCP Model Discovery Tool Tests the .sim file parser and FEA model discovery functionality. """ import pytest from pathlib import Path import sys # Add project root to path project_root = Path(__file__).parent.parent.parent.parent sys.path.insert(0, str(project_root)) from mcp_server.tools.model_discovery import ( discover_fea_model, format_discovery_result_for_llm, SimFileParser ) class TestSimFileParser: """Test the SimFileParser class""" @pytest.fixture def example_sim_path(self): """Path to example .sim file""" return project_root / "examples" / "test_bracket.sim" def test_parser_initialization(self, example_sim_path): """Test that parser initializes correctly""" parser = SimFileParser(example_sim_path) assert parser.sim_path.exists() assert parser.tree is not None assert parser.root is not None def test_parser_file_not_found(self): """Test error handling for missing file""" with pytest.raises(FileNotFoundError): SimFileParser("/nonexistent/path/file.sim") def test_parser_invalid_extension(self): """Test error handling for non-.sim file""" with pytest.raises(ValueError): SimFileParser(project_root / "README.md") def test_extract_solutions(self, example_sim_path): """Test solution extraction""" parser = SimFileParser(example_sim_path) solutions = parser.extract_solutions() assert len(solutions) > 0 assert solutions[0]['name'] == 'Structural Analysis 1' assert solutions[0]['type'] == 'Static Structural' assert solutions[0]['solver'] == 'NX Nastran' def test_extract_expressions(self, example_sim_path): """Test expression extraction""" parser = SimFileParser(example_sim_path) expressions = parser.extract_expressions() assert len(expressions) > 0 # Check for expected expressions expr_names = [e['name'] for e in expressions] assert 'wall_thickness' in expr_names assert 'hole_diameter' in expr_names assert 'rib_spacing' in expr_names # Check expression values wall_thickness = next(e for e in expressions if e['name'] == 'wall_thickness') assert wall_thickness['value'] == '5.0' assert wall_thickness['units'] == 'mm' def test_extract_fem_info(self, example_sim_path): """Test FEM information extraction""" parser = SimFileParser(example_sim_path) fem_info = parser.extract_fem_info() # Check mesh info assert 'mesh' in fem_info assert fem_info['mesh']['name'] == 'Bracket Mesh' assert fem_info['mesh']['node_count'] == '8234' assert fem_info['mesh']['element_count'] == '4521' # Check materials assert len(fem_info['materials']) > 0 assert fem_info['materials'][0]['name'] == 'Aluminum 6061-T6' # Check loads assert len(fem_info['loads']) > 0 assert fem_info['loads'][0]['name'] == 'Applied Force' # Check constraints assert len(fem_info['constraints']) > 0 assert fem_info['constraints'][0]['name'] == 'Fixed Support' class TestDiscoverFEAModel: """Test the main discover_fea_model function""" @pytest.fixture def example_sim_path(self): """Path to example .sim file""" return str(project_root / "examples" / "test_bracket.sim") def test_successful_discovery(self, example_sim_path): """Test successful model discovery""" result = discover_fea_model(example_sim_path) assert result['status'] == 'success' assert result['file_exists'] is True assert 'solutions' in result assert 'expressions' in result assert 'fem_info' in result assert 'summary' in result # Check summary statistics assert result['summary']['solution_count'] >= 1 assert result['summary']['expression_count'] >= 3 def test_file_not_found_error(self): """Test error handling for missing file""" result = discover_fea_model("/nonexistent/file.sim") assert result['status'] == 'error' assert result['error_type'] == 'file_not_found' assert 'message' in result assert 'suggestion' in result def test_result_structure(self, example_sim_path): """Test that result has expected structure""" result = discover_fea_model(example_sim_path) # Check top-level keys expected_keys = ['status', 'sim_file', 'file_exists', 'solutions', 'expressions', 'fem_info', 'linked_files', 'metadata', 'summary'] for key in expected_keys: assert key in result, f"Missing key: {key}" # Check summary keys expected_summary_keys = ['solution_count', 'expression_count', 'material_count', 'load_count', 'constraint_count'] for key in expected_summary_keys: assert key in result['summary'], f"Missing summary key: {key}" class TestFormatDiscoveryResult: """Test the Markdown formatting function""" @pytest.fixture def example_sim_path(self): """Path to example .sim file""" return str(project_root / "examples" / "test_bracket.sim") def test_format_success_result(self, example_sim_path): """Test formatting of successful discovery""" result = discover_fea_model(example_sim_path) formatted = format_discovery_result_for_llm(result) assert isinstance(formatted, str) assert '# FEA Model Analysis' in formatted assert 'Solutions' in formatted assert 'Expressions' in formatted assert 'wall_thickness' in formatted def test_format_error_result(self): """Test formatting of error result""" result = discover_fea_model("/nonexistent/file.sim") formatted = format_discovery_result_for_llm(result) assert isinstance(formatted, str) assert '❌' in formatted or 'Error' in formatted assert result['message'] in formatted # Integration test def test_end_to_end_workflow(): """ Test the complete workflow: 1. Discover model 2. Format for LLM 3. Verify output is useful """ example_sim = str(project_root / "examples" / "test_bracket.sim") # Step 1: Discover result = discover_fea_model(example_sim) assert result['status'] == 'success' # Step 2: Format formatted = format_discovery_result_for_llm(result) assert len(formatted) > 100 # Should be substantial output # Step 3: Verify key information is present assert 'wall_thickness' in formatted assert 'Aluminum' in formatted assert 'Static Structural' in formatted print("\n" + "="*60) print("INTEGRATION TEST OUTPUT:") print("="*60) print(formatted) print("="*60) if __name__ == "__main__": # Run tests with pytest pytest.main([__file__, "-v", "-s"])