@@ -79,6 +79,28 @@ def test_node_type_additional_properties_default() -> None:
7979 assert node_type .additional_properties is True
8080
8181
82+ def test_property_type_initalization () -> None :
83+ prop = PropertyType (name = "email" , type = "STRING" )
84+ assert prop .name == "email"
85+ assert prop .type == "STRING"
86+ assert prop .required is False
87+
88+
89+ def test_property_type_with_required_true () -> None :
90+ prop = PropertyType (name = "id" , type = "INTEGER" , required = True )
91+ assert prop .required is True
92+
93+
94+ def test_property_type_is_frozen () -> None :
95+ prop = PropertyType (name = "email" , type = "STRING" , required = False )
96+
97+ with pytest .raises (ValidationError ):
98+ prop .name = "other"
99+
100+ with pytest .raises (ValidationError ):
101+ prop .required = True
102+
103+
82104def test_relationship_type_initialization_from_string () -> None :
83105 relationship_type = RelationshipType .model_validate ("REL" )
84106 assert isinstance (relationship_type , RelationshipType )
@@ -730,6 +752,55 @@ def schema_json_with_null_constraints() -> str:
730752 """
731753
732754
755+ @pytest .fixture
756+ def schema_json_with_required_properties () -> str :
757+ return """
758+ {
759+ "node_types": [
760+ {
761+ "label": "Person",
762+ "properties": [
763+ {"name": "name", "type": "STRING", "required": true},
764+ {"name": "email", "type": "STRING", "required": false},
765+ {"name": "phone", "type": "STRING"}
766+ ]
767+ }
768+ ],
769+ "relationship_types": [
770+ {"label": "KNOWS"}
771+ ],
772+ "patterns": [
773+ ["Person", "KNOWS", "Person"]
774+ ]
775+ }
776+ """
777+
778+
779+ @pytest .fixture
780+ def schema_json_with_string_required_values () -> str :
781+ return """
782+ {
783+ "node_types": [
784+ {
785+ "label": "Person",
786+ "properties": [
787+ {"name": "name", "type": "STRING", "required": "true"},
788+ {"name": "email", "type": "STRING", "required": "yes"},
789+ {"name": "phone", "type": "STRING", "required": "false"},
790+ {"name": "address", "type": "STRING", "required": "no"}
791+ ]
792+ }
793+ ],
794+ "relationship_types": [
795+ {"label": "KNOWS"}
796+ ],
797+ "patterns": [
798+ ["Person", "KNOWS", "Person"]
799+ ]
800+ }
801+ """
802+
803+
733804@pytest .fixture
734805def invalid_schema_json () -> str :
735806 return """
@@ -1388,6 +1459,114 @@ def test_clean_json_content_plain_json(
13881459 assert cleaned == '{"node_types": [{"label": "Person"}]}'
13891460
13901461
1462+ def test_filter_properties_required_field_valid_true (
1463+ schema_from_text : SchemaFromTextExtractor ,
1464+ ) -> None :
1465+ node_types = [
1466+ {
1467+ "label" : "Person" ,
1468+ "properties" : [{"name" : "name" , "type" : "STRING" , "required" : True }],
1469+ }
1470+ ]
1471+ result = schema_from_text ._filter_properties_required_field (node_types )
1472+ assert result [0 ]["properties" ][0 ]["required" ] is True
1473+
1474+
1475+ def test_filter_properties_required_field_valid_false (
1476+ schema_from_text : SchemaFromTextExtractor ,
1477+ ) -> None :
1478+ node_types = [
1479+ {
1480+ "label" : "Person" ,
1481+ "properties" : [{"name" : "name" , "type" : "STRING" , "required" : False }],
1482+ }
1483+ ]
1484+ result = schema_from_text ._filter_properties_required_field (node_types )
1485+ assert result [0 ]["properties" ][0 ]["required" ] is False
1486+
1487+
1488+ def test_filter_properties_required_field_string (
1489+ schema_from_text : SchemaFromTextExtractor ,
1490+ ) -> None :
1491+ node_types = [
1492+ {
1493+ "label" : "Person" ,
1494+ "properties" : [
1495+ {"name" : "prop1" , "type" : "STRING" , "required" : "true" },
1496+ {"name" : "prop2" , "type" : "STRING" , "required" : "yes" },
1497+ {"name" : "prop3" , "type" : "STRING" , "required" : "1" },
1498+ {"name" : "prop4" , "type" : "STRING" , "required" : "TRUE" },
1499+ ],
1500+ }
1501+ ]
1502+ result = schema_from_text ._filter_properties_required_field (node_types )
1503+ for prop in result [0 ]["properties" ]:
1504+ assert prop ["required" ] is True
1505+ node_types = [
1506+ {
1507+ "label" : "Person" ,
1508+ "properties" : [
1509+ {"name" : "prop1" , "type" : "STRING" , "required" : "false" },
1510+ {"name" : "prop2" , "type" : "STRING" , "required" : "no" },
1511+ {"name" : "prop3" , "type" : "STRING" , "required" : "0" },
1512+ {"name" : "prop4" , "type" : "STRING" , "required" : "FALSE" },
1513+ ],
1514+ }
1515+ ]
1516+ result = schema_from_text ._filter_properties_required_field (node_types )
1517+ for prop in result [0 ]["properties" ]:
1518+ assert prop ["required" ] is False
1519+
1520+
1521+ def test_filter_properties_required_field_invalid_string (
1522+ schema_from_text : SchemaFromTextExtractor ,
1523+ ) -> None :
1524+ node_types = [
1525+ {
1526+ "label" : "Person" ,
1527+ "properties" : [
1528+ {"name" : "name" , "type" : "STRING" , "required" : "mandatory" },
1529+ {"name" : "email" , "type" : "STRING" , "required" : "always" },
1530+ ],
1531+ }
1532+ ]
1533+ result = schema_from_text ._filter_properties_required_field (node_types )
1534+
1535+ assert "required" not in result [0 ]["properties" ][0 ]
1536+ assert "required" not in result [0 ]["properties" ][1 ]
1537+
1538+
1539+ def test_filter_properties_required_field_invalid_type (
1540+ schema_from_text : SchemaFromTextExtractor ,
1541+ ) -> None :
1542+ node_types = [
1543+ {
1544+ "label" : "Person" ,
1545+ "properties" : [
1546+ {"name" : "prop1" , "type" : "STRING" , "required" : 1 },
1547+ {"name" : "prop2" , "type" : "STRING" , "required" : []},
1548+ {"name" : "prop3" , "type" : "STRING" , "required" : {"value" : True }},
1549+ ],
1550+ }
1551+ ]
1552+ result = schema_from_text ._filter_properties_required_field (node_types )
1553+ for prop in result [0 ]["properties" ]:
1554+ assert "required" not in prop
1555+
1556+
1557+ def test_filter_properties_required_field_missing (
1558+ schema_from_text : SchemaFromTextExtractor ,
1559+ ) -> None :
1560+ node_types = [
1561+ {
1562+ "label" : "Person" ,
1563+ "properties" : [{"name" : "name" , "type" : "STRING" }],
1564+ }
1565+ ]
1566+ result = schema_from_text ._filter_properties_required_field (node_types )
1567+ assert "required" not in result [0 ]["properties" ][0 ]
1568+
1569+
13911570@pytest .mark .asyncio
13921571@patch ("neo4j_graphrag.experimental.components.schema.get_structured_schema" )
13931572async def test_schema_from_existing_graph (mock_get_structured_schema : Mock ) -> None :
0 commit comments