Skip to main content
Glama
test_facet_tools.py21.6 kB
"""Tests for facet management tools.""" import pytest from ifctester import ids 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.specification import add_specification from ids_mcp_server.tools.document import create_ids from ids_mcp_server.session.storage import get_session_storage from fastmcp.exceptions import ToolError # Entity Facet Tests @pytest.mark.asyncio async def test_add_entity_facet_to_applicability(mock_context): """Test adding entity facet to applicability.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) assert result["status"] == "added" assert result["facet_type"] == "entity" # Verify facet added storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.applicability) == 1 assert isinstance(spec.applicability[0], ids.Entity) assert spec.applicability[0].name == "IFCWALL" @pytest.mark.asyncio async def test_add_entity_facet_with_predefined_type(mock_context): """Test adding entity facet with predefined type.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context, predefined_type="SOLIDWALL" ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] entity = spec.applicability[0] assert entity.predefinedType == "SOLIDWALL" @pytest.mark.asyncio async def test_add_entity_facet_to_requirements(mock_context): """Test adding entity facet to requirements.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_entity_facet( spec_id="S1", location="requirements", entity_name="IFCDOOR", ctx=mock_context ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 1 @pytest.mark.asyncio async def test_add_entity_facet_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_entity_facet( spec_id="S1", location="invalid", entity_name="IFCWALL", ctx=mock_context ) assert "Invalid location" in str(exc_info.value) # Property Facet Tests @pytest.mark.asyncio async def test_add_property_facet_minimal(mock_context): """Test adding property facet with minimal fields.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set="Pset_WallCommon" ) assert result["status"] == "added" assert result["facet_type"] == "property" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 1 assert isinstance(spec.requirements[0], ids.Property) assert spec.requirements[0].baseName == "FireRating" @pytest.mark.asyncio async def test_add_property_facet_complete(mock_context): """Test adding property facet with all fields.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_property_facet( spec_id="S1", location="requirements", property_name="LoadBearing", ctx=mock_context, property_set="Pset_WallCommon", data_type="IFCBOOLEAN", value="TRUE", cardinality="required" ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] assert prop.baseName == "LoadBearing" assert prop.propertySet == "Pset_WallCommon" assert prop.dataType == "IFCBOOLEAN" assert prop.value == "TRUE" assert prop.cardinality == "required" # Attribute Facet Tests @pytest.mark.asyncio async def test_add_attribute_facet(mock_context): """Test adding 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") result = await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Name", ctx=mock_context, value="External Wall" ) assert result["status"] == "added" assert result["facet_type"] == "attribute" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 1 assert isinstance(spec.requirements[0], ids.Attribute) assert spec.requirements[0].name == "Name" assert spec.requirements[0].value == "External Wall" @pytest.mark.asyncio async def test_add_attribute_facet_with_cardinality(mock_context): """Test adding attribute facet with cardinality.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_attribute_facet( spec_id="S1", location="requirements", attribute_name="Description", ctx=mock_context, cardinality="optional" ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] attr = spec.requirements[0] assert attr.cardinality == "optional" # Classification Facet Tests @pytest.mark.asyncio async def test_add_classification_facet(mock_context): """Test adding classification facet.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_classification_facet( spec_id="S1", location="requirements", classification_value="Ss_25_10_20", ctx=mock_context, classification_system="Uniclass" ) assert result["status"] == "added" assert result["facet_type"] == "classification" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 1 assert isinstance(spec.requirements[0], ids.Classification) assert spec.requirements[0].value == "Ss_25_10_20" assert spec.requirements[0].system == "Uniclass" @pytest.mark.asyncio async def test_add_classification_facet_with_uri(mock_context): """Test adding classification with URI system.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_classification_facet( spec_id="S1", location="applicability", classification_value="23.30.10", ctx=mock_context, classification_system="https://identifier.buildingsmart.org/uri/buildingsmart/ifc-4.3/class/IfcWall" ) assert result["status"] == "added" # Material Facet Tests @pytest.mark.asyncio async def test_add_material_facet(mock_context): """Test adding material facet.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_material_facet( spec_id="S1", location="requirements", material_value="Concrete", ctx=mock_context ) assert result["status"] == "added" assert result["facet_type"] == "material" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 1 assert isinstance(spec.requirements[0], ids.Material) assert spec.requirements[0].value == "Concrete" @pytest.mark.asyncio async def test_add_material_facet_with_cardinality(mock_context): """Test adding material facet with cardinality.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_material_facet( spec_id="S1", location="requirements", material_value="Steel", ctx=mock_context, cardinality="prohibited" ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] material = spec.requirements[0] assert material.cardinality == "prohibited" # PartOf Facet Tests @pytest.mark.asyncio async def test_add_partof_facet(mock_context): """Test adding partOf facet.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_partof_facet( spec_id="S1", location="applicability", relation="IFCRELCONTAINEDINSPATIALSTRUCTURE", parent_entity="IFCSPACE", ctx=mock_context ) assert result["status"] == "added" assert result["facet_type"] == "partof" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.applicability) == 1 assert isinstance(spec.applicability[0], ids.PartOf) assert spec.applicability[0].relation == "IFCRELCONTAINEDINSPATIALSTRUCTURE" @pytest.mark.asyncio async def test_add_partof_facet_with_predefined_type(mock_context): """Test adding partOf facet with parent predefined type.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") result = await add_partof_facet( spec_id="S1", location="requirements", relation="IFCRELAGGREGATES", parent_entity="IFCBUILDING", ctx=mock_context, parent_predefined_type="RESIDENTIAL", cardinality="required" ) assert result["status"] == "added" storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] part_of = spec.requirements[0] assert isinstance(part_of, ids.PartOf) assert part_of.predefinedType == "RESIDENTIAL" assert part_of.cardinality == "required" # Test finding specification by name @pytest.mark.asyncio async def test_facet_finds_spec_by_name(mock_context): """Test that facets can find specification by name.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="My Specification", ifc_versions=["IFC4"], ctx=mock_context) # Should find by name result = await add_entity_facet( spec_id="My Specification", location="applicability", entity_name="IFCWALL", ctx=mock_context ) assert result["status"] == "added" @pytest.mark.asyncio async def test_facet_spec_not_found(mock_context): """Test that missing specification raises error.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Spec 1", ifc_versions=["IFC4"], ctx=mock_context) with pytest.raises(ToolError) as exc_info: await add_entity_facet( spec_id="NonExistent", location="applicability", entity_name="IFCWALL", ctx=mock_context ) assert "Specification not found" in str(exc_info.value) # Test combining multiple facets @pytest.mark.asyncio async def test_multiple_facets_same_specification(mock_context): """Test adding multiple different facets to same specification.""" 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 await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) # Add property to requirements await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set="Pset_WallCommon" ) # Add material to requirements await add_material_facet( spec_id="S1", location="requirements", material_value="Concrete", ctx=mock_context ) # Verify all facets added storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.applicability) == 1 assert len(spec.requirements) == 2 assert isinstance(spec.applicability[0], ids.Entity) assert isinstance(spec.requirements[0], ids.Property) assert isinstance(spec.requirements[1], ids.Material) # Error handling tests for facets @pytest.mark.asyncio async def test_entity_facet_generic_error(mock_context): """Test entity facet handles generic errors.""" await create_ids(title="Test IDS", ctx=mock_context) # Try to add facet to non-existent spec with pytest.raises(ToolError) as exc_info: await add_entity_facet( spec_id="NonExistent", location="applicability", entity_name="IFCWALL", ctx=mock_context ) assert "Specification not found" in str(exc_info.value) @pytest.mark.asyncio async def test_property_facet_invalid_location_error(mock_context): """Test property facet with invalid location.""" 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_property_facet( spec_id="S1", location="invalid_location", property_name="FireRating", ctx=mock_context, property_set="Pset_WallCommon" ) assert "Invalid location" in str(exc_info.value) @pytest.mark.asyncio async def test_attribute_facet_invalid_location_error(mock_context): """Test attribute facet with invalid location.""" 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_attribute_facet( spec_id="S1", location="wrong", attribute_name="Name", ctx=mock_context ) assert "Invalid location" in str(exc_info.value) @pytest.mark.asyncio async def test_classification_facet_invalid_location_error(mock_context): """Test classification facet with invalid location.""" 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_classification_facet( spec_id="S1", location="other", classification_value="Test", ctx=mock_context ) assert "Invalid location" in str(exc_info.value) @pytest.mark.asyncio async def test_material_facet_invalid_location_error(mock_context): """Test material facet with invalid location.""" 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_material_facet( spec_id="S1", location="somewhere", material_value="Concrete", ctx=mock_context ) assert "Invalid location" in str(exc_info.value) # Validation Error Tests (IDS 1.0 Constraints) @pytest.mark.asyncio async def test_add_second_entity_facet_to_applicability_raises_error(mock_context): """Test that adding second entity facet to applicability 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") # First entity - should succeed await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCWALL", ctx=mock_context ) # Second entity - should fail with clear error with pytest.raises(ToolError) as exc_info: await add_entity_facet( spec_id="S1", location="applicability", entity_name="IFCDOOR", ctx=mock_context ) error_msg = str(exc_info.value) assert "IDS 1.0 XSD constraint violation" in error_msg assert "Only ONE entity facet" in error_msg assert "WORKAROUND" in error_msg assert "separate specification" in error_msg @pytest.mark.asyncio async def test_add_multiple_entity_facets_to_requirements_allowed(mock_context): """Test that multiple entity facets in requirements is allowed.""" await create_ids(title="Test IDS", ctx=mock_context) await add_specification(name="Test Spec", ifc_versions=["IFC4"], ctx=mock_context, identifier="S1") # Multiple entity facets in requirements - should succeed await add_entity_facet( spec_id="S1", location="requirements", entity_name="IFCWALL", ctx=mock_context ) await add_entity_facet( spec_id="S1", location="requirements", entity_name="IFCDOOR", ctx=mock_context ) # Verify both added storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] assert len(spec.requirements) == 2 assert isinstance(spec.requirements[0], ids.Entity) assert isinstance(spec.requirements[1], ids.Entity) @pytest.mark.asyncio async def test_add_property_facet_without_property_set_raises_error(mock_context): """Test that property facet without property_set 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") # Try to add property without property_set with pytest.raises(ToolError) as exc_info: await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set=None # Missing! ) error_msg = str(exc_info.value) assert "property_set" in error_msg.lower() assert "required" in error_msg.lower() assert "COMMON PROPERTY SETS" in error_msg assert "Pset_WallCommon" in error_msg @pytest.mark.asyncio async def test_add_property_facet_with_empty_property_set_raises_error(mock_context): """Test that property facet with empty property_set 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") # Try to add property with empty property_set with pytest.raises(ToolError) as exc_info: await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set="" # Empty! ) assert "property_set" in str(exc_info.value).lower() @pytest.mark.asyncio async def test_add_property_facet_with_property_set_succeeds(mock_context): """Test that property facet with property_set succeeds.""" 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 with proper property_set result = await add_property_facet( spec_id="S1", location="requirements", property_name="FireRating", ctx=mock_context, property_set="Pset_WallCommon" # Provided! ) assert result["status"] == "added" # Verify in session storage = get_session_storage() spec = storage.get(mock_context.session_id).ids_obj.specifications[0] prop = spec.requirements[0] assert prop.baseName == "FireRating" assert prop.propertySet == "Pset_WallCommon"

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