Skip to main content
Glama
test_error_handling.py9.27 kB
"""Component tests for error handling across all tools. Tests error paths and exception handling to achieve 95%+ coverage. """ import pytest from unittest.mock import AsyncMock, MagicMock, patch from fastmcp.exceptions import ToolError from ids_mcp_server.tools.document import create_ids, load_ids, export_ids, get_ids_info from ids_mcp_server.tools.facets import ( add_entity_facet, add_property_facet, add_attribute_facet, add_classification_facet, add_material_facet, add_partof_facet ) from ids_mcp_server.tools.validation import validate_ids from ids_mcp_server.tools.specification import add_specification from ids_mcp_server.session.storage import get_session_storage @pytest.fixture def mock_context(): """Provide mock FastMCP Context for testing.""" ctx = MagicMock() ctx.session_id = "test-error-session" ctx.info = AsyncMock() ctx.debug = AsyncMock() ctx.warning = AsyncMock() ctx.error = AsyncMock() return ctx # Document Tool Error Paths @pytest.mark.asyncio async def test_create_ids_exception_handling(mock_context): """Test create_ids handles unexpected exceptions.""" # Patch ids.Ids to raise an exception with patch('ids_mcp_server.tools.document.ids.Ids', side_effect=RuntimeError("Unexpected error")): with pytest.raises(ToolError, match="Failed to create IDS"): await create_ids(title="Test", ctx=mock_context) # Verify error was logged mock_context.error.assert_called_once() @pytest.mark.asyncio async def test_load_ids_file_not_found(mock_context): """Test load_ids handles file not found.""" with pytest.raises(ToolError, match="File not found"): await load_ids( source="/nonexistent/file.ids", source_type="file", ctx=mock_context ) @pytest.mark.asyncio async def test_load_ids_invalid_xml(mock_context): """Test load_ids handles invalid XML.""" with pytest.raises(ToolError, match="Failed to load IDS"): await load_ids( source="<invalid>xml", source_type="string", ctx=mock_context ) @pytest.mark.asyncio async def test_export_ids_to_string(mock_context): """Test export_ids exports to string successfully.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Export should work result = await export_ids(ctx=mock_context) assert "xml" in result assert "<ids" in result["xml"] assert "Test</title>" in result["xml"] @pytest.mark.asyncio async def test_get_ids_info_exception_handling(mock_context): """Test get_ids_info handles exceptions.""" # Create a session with corrupt data storage = get_session_storage() # Patch the storage to raise an exception with patch.object(storage, 'get', side_effect=RuntimeError("Storage error")): with pytest.raises(ToolError, match="Failed to get IDS info"): await get_ids_info(ctx=mock_context) # Facet Tool Error Paths @pytest.mark.asyncio async def test_entity_facet_exception_in_ifctester(mock_context): """Test add_entity_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Patch ids.Entity to raise an exception with patch('ids_mcp_server.tools.facets.ids.Entity', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add entity facet"): await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) @pytest.mark.asyncio async def test_property_facet_exception_in_ifctester(mock_context): """Test add_property_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with patch('ids_mcp_server.tools.facets.ids.Property', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add property facet"): await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", property_set="Pset_WallCommon", ctx=mock_context ) @pytest.mark.asyncio async def test_attribute_facet_exception_in_ifctester(mock_context): """Test add_attribute_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with patch('ids_mcp_server.tools.facets.ids.Attribute', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add attribute facet"): await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Name", ctx=mock_context ) @pytest.mark.asyncio async def test_classification_facet_exception_in_ifctester(mock_context): """Test add_classification_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with patch('ids_mcp_server.tools.facets.ids.Classification', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add classification facet"): await add_classification_facet( spec_id="S1", location="requirements", classification_value="Ss_25_10_20", classification_system="Uniclass", ctx=mock_context ) @pytest.mark.asyncio async def test_material_facet_exception_in_ifctester(mock_context): """Test add_material_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with patch('ids_mcp_server.tools.facets.ids.Material', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add material facet"): await add_material_facet( spec_id="S1", location="requirements", material_value="Concrete", ctx=mock_context ) @pytest.mark.asyncio async def test_partof_facet_invalid_location(mock_context): """Test add_partof_facet handles invalid location.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with pytest.raises(ToolError, match="Invalid location"): await add_partof_facet( spec_id="S1", location="invalid_location", relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", parent_entity="IFCSPACE", ctx=mock_context ) @pytest.mark.asyncio async def test_partof_facet_exception_in_ifctester(mock_context): """Test add_partof_facet handles IfcTester exceptions.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with patch('ids_mcp_server.tools.facets.ids.PartOf', side_effect=RuntimeError("IfcTester error")): with pytest.raises(ToolError, match="Failed to add partOf facet"): await add_partof_facet( spec_id="S1", location="applicability", relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", parent_entity="IFCSPACE", ctx=mock_context ) # Validation Tool Error Paths @pytest.mark.asyncio async def test_validate_ids_xsd_validation_failure(mock_context): """Test validate_ids handles XSD validation failures.""" await create_ids(title="Test", ctx=mock_context) await add_specification(name="Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Patch from_string to raise XSD validation error with patch('ids_mcp_server.tools.validation.ids.from_string', side_effect=Exception("XSD validation failed")): result = await validate_ids(ctx=mock_context) # Should still return result, but with xsd_valid=False and errors assert result["valid"] is False assert result["details"]["xsd_valid"] is False assert any("XSD validation failed" in err for err in result["errors"]) @pytest.mark.asyncio async def test_validate_ids_exception_handling(mock_context): """Test validate_ids handles unexpected exceptions.""" # Patch get_or_create_session to raise an exception with patch('ids_mcp_server.tools.validation.get_or_create_session', side_effect=RuntimeError("Unexpected")): with pytest.raises(ToolError, match="Validation error"): await validate_ids(ctx=mock_context)

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/vinnividivicci/ifc-ids-mcp'

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