Skip to main content
Glama
test_restriction_tools.py16.1 kB
"""RED: Tests for restriction management tools.""" import pytest from ifctester import ids from ids_mcp_server.tools.restrictions import ( add_enumeration_restriction, add_pattern_restriction, add_bounds_restriction, add_length_restriction ) from ids_mcp_server.tools.document import create_ids from ids_mcp_server.tools.specification import add_specification from ids_mcp_server.tools.facets import add_property_facet, add_attribute_facet, add_entity_facet from ids_mcp_server.session.storage import get_session_storage from fastmcp.exceptions import ToolError # Enumeration Restriction Tests @pytest.mark.asyncio async def test_add_enumeration_restriction_to_property(mock_context): """Test adding enumeration restriction to property facet.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Add property facet to requirements await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set="Pset_WallCommon" ) # Add enumeration restriction to property value result = await add_enumeration_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", values=["REI30", "REI60", "REI90"], ctx=mock_context, location="requirements" ) assert result["status"] == "added" assert result["restriction_type"] == "enumeration" # Verify restriction with IfcTester storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] assert isinstance(prop.value, ids.Restriction) # IfcTester stores base without 'xs:' prefix internally assert prop.value.base == "string" # Check enumeration values are stored assert hasattr(prop.value, "enumeration") or "enumeration" in getattr(prop.value, "options", {}) @pytest.mark.asyncio async def test_enumeration_restriction_exports_to_xml(mock_context): """Test that enumeration restriction exports to valid XML.""" from ids_mcp_server.tools.document import export_ids await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Add entity to applicability (required by IDS XSD) await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) await add_property_facet( spec_id="S1", location="requirements", property_name="LoadBearing", ctx=mock_context, property_set="Pset_WallCommon" ) await add_enumeration_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", values=["TRUE", "FALSE"], ctx=mock_context ) # Export to XML (skip XSD validation due to IfcTester namespace bug) result = await export_ids(ctx=mock_context, validate=False) # Verify XML contains restriction elements assert result["status"] == "exported" assert "<xs:restriction" in result["xml"] assert "<xs:enumeration" in result["xml"] assert 'value="TRUE"' in result["xml"] assert 'value="FALSE"' in result["xml"] @pytest.mark.asyncio async def test_enumeration_restriction_invalid_facet_index(mock_context): """Test that invalid facet index raises error.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Don't add any facets with pytest.raises(ToolError) as exc_info: await add_enumeration_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", values=["A", "B"], ctx=mock_context ) assert "facet index" in str(exc_info.value).lower() or "out of range" in str(exc_info.value).lower() # Pattern Restriction Tests @pytest.mark.asyncio async def test_add_pattern_restriction_to_attribute(mock_context): """Test adding pattern restriction to attribute facet.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Name", ctx=mock_context ) result = await add_pattern_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", pattern="EW-[0-9]{3}", ctx=mock_context ) assert result["status"] == "added" assert result["restriction_type"] == "pattern" # Verify with IfcTester storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] attr = spec.requirements[0] assert isinstance(attr.value, ids.Restriction) # IfcTester stores base without 'xs:' prefix internally assert attr.value.base == "string" # Check pattern is stored assert hasattr(attr.value, "pattern") or "pattern" in getattr(attr.value, "options", {}) @pytest.mark.asyncio async def test_pattern_restriction_exports_to_xml(mock_context): """Test that pattern restriction exports to valid XML.""" from ids_mcp_server.tools.document import export_ids await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Add entity to applicability (required by IDS XSD) await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Tag", ctx=mock_context ) await add_pattern_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", pattern="[A-Z]{2}-[0-9]{4}", ctx=mock_context ) # Export to XML (skip XSD validation due to IfcTester namespace bug) result = await export_ids(ctx=mock_context, validate=False) # Verify XML contains restriction elements assert result["status"] == "exported" assert "<xs:restriction" in result["xml"] assert "<xs:pattern" in result["xml"] assert 'value="[A-Z]{2}-[0-9]{4}"' in result["xml"] # Bounds Restriction Tests @pytest.mark.asyncio async def test_add_bounds_restriction_min_max_inclusive(mock_context): """Test adding bounds restriction with min/max inclusive.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_property_facet( spec_id="S1", location="requirements", property_name="Height", ctx=mock_context, property_set="Pset_WallCommon", data_type="IFCREAL" ) result = await add_bounds_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:double", ctx=mock_context, min_inclusive=2.4, max_inclusive=3.0 ) assert result["status"] == "added" assert result["restriction_type"] == "bounds" # Verify with IfcTester storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] assert isinstance(prop.value, ids.Restriction) # IfcTester stores base without 'xs:' prefix internally assert prop.value.base == "double" # Check bounds are stored options = getattr(prop.value, "options", {}) assert "minInclusive" in options or hasattr(prop.value, "minInclusive") assert "maxInclusive" in options or hasattr(prop.value, "maxInclusive") @pytest.mark.asyncio async def test_add_bounds_restriction_exclusive(mock_context): """Test adding bounds restriction with exclusive bounds.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_property_facet( spec_id="S1", location="requirements", property_name="Temperature", ctx=mock_context, property_set="Pset_Common", data_type="IFCREAL" ) result = await add_bounds_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:double", ctx=mock_context, min_exclusive=0.0, max_exclusive=100.0 ) assert result["status"] == "added" # Verify with IfcTester storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] assert isinstance(prop.value, ids.Restriction) @pytest.mark.asyncio async def test_bounds_restriction_exports_to_xml(mock_context): """Test that bounds restriction exports to valid XML.""" from ids_mcp_server.tools.document import export_ids await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Add entity to applicability (required by IDS XSD) await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) await add_property_facet( spec_id="S1", location="requirements", property_name="Width", ctx=mock_context, property_set="Pset_WallCommon", data_type="IFCREAL" ) await add_bounds_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:double", ctx=mock_context, min_inclusive=0.1, max_inclusive=0.5 ) # Export to XML (skip XSD validation due to IfcTester namespace bug) result = await export_ids(ctx=mock_context, validate=False) # Verify XML contains restriction elements assert result["status"] == "exported" assert "<xs:restriction" in result["xml"] assert "<xs:minInclusive" in result["xml"] or "<xs:maxInclusive" in result["xml"] # Length Restriction Tests @pytest.mark.asyncio async def test_add_length_restriction_min_max(mock_context): """Test adding length restriction with min/max.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Description", ctx=mock_context ) result = await add_length_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", ctx=mock_context, min_length=5, max_length=50 ) assert result["status"] == "added" assert result["restriction_type"] == "length" # Verify with IfcTester storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] attr = spec.requirements[0] assert isinstance(attr.value, ids.Restriction) # IfcTester stores base without 'xs:' prefix internally assert attr.value.base == "string" @pytest.mark.asyncio async def test_add_length_restriction_exact(mock_context): """Test adding exact length restriction.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Tag", ctx=mock_context ) result = await add_length_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", ctx=mock_context, length=10 ) assert result["status"] == "added" @pytest.mark.asyncio async def test_length_restriction_exports_to_xml(mock_context): """Test that length restriction exports to valid XML.""" from ids_mcp_server.tools.document import export_ids await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Add entity to applicability (required by IDS XSD) await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Name", ctx=mock_context ) await add_length_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", ctx=mock_context, min_length=1, max_length=100 ) # Export to XML (skip XSD validation due to IfcTester namespace bug) result = await export_ids(ctx=mock_context, validate=False) # Verify XML contains restriction elements assert result["status"] == "exported" assert "<xs:restriction" in result["xml"] assert "<xs:minLength" in result["xml"] or "<xs:maxLength" in result["xml"] # Error Handling Tests @pytest.mark.asyncio async def test_restriction_spec_not_found(mock_context): """Test that missing specification raises error.""" await create_ids(title="Test IDS", ctx=mock_context) with pytest.raises(ToolError) as exc_info: await add_enumeration_restriction( spec_id="NonExistent", facet_index=0, parameter_name="value", base_type="xs:string", values=["A"], ctx=mock_context ) assert "Specification not found" in str(exc_info.value) @pytest.mark.asyncio async def test_restriction_invalid_location(mock_context): """Test that invalid location raises error.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") with pytest.raises(ToolError) as exc_info: await add_pattern_restriction( spec_id="S1", facet_index=0, parameter_name="value", base_type="xs:string", pattern=".*", ctx=mock_context, location="invalid" ) assert "Invalid location" in str(exc_info.value) # Test applying restrictions to different parameters @pytest.mark.asyncio async def test_restriction_on_property_set_parameter(mock_context): """Test adding restriction to propertySet parameter.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") await add_property_facet( spec_id="S1", location="requirements", property_name=".*", ctx=mock_context, property_set="Pset_Common" ) # Restrict property set name result = await add_pattern_restriction( spec_id="S1", facet_index=0, parameter_name="propertySet", base_type="xs:string", pattern="Pset_.*", ctx=mock_context ) assert result["status"] == "added" # Verify restriction applied to propertySet, not value storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] # The restriction should be on propertySet parameter assert hasattr(prop, "propertySet") and isinstance(prop.propertySet, ids.Restriction)

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