@@ -789,3 +789,165 @@ def test_extended_attributes_in_metadata(self):
789789 assert '@vspec(element: ATTRIBUTE, fqn: "Vehicle.Info.Model"' in schema_str
790790 assert '{key: "customMetadata", value: "test_value"}' in schema_str
791791 assert '{key: "anotherAttribute", value: "42"}' in schema_str
792+
793+ def test_empty_branches_are_skipped (self , caplog ):
794+ """Test that empty branches (branches with no children) are skipped during export."""
795+ # Load vspec with empty branches
796+ tree , _ = get_trees (
797+ vspec = Path ("tests/vspec/test_s2dm/test_empty_branches.vspec" ),
798+ include_dirs = (),
799+ aborts = (),
800+ strict = False ,
801+ extended_attributes = (),
802+ quantities = (Path ("tests/vspec/test_s2dm/test_quantities.yaml" ),),
803+ units = (Path ("tests/vspec/test_s2dm/test_units.yaml" ),),
804+ overlays = (),
805+ expand = False ,
806+ )
807+
808+ # Generate schema
809+ schema , unit_metadata , allowed_metadata , vspec_comments = generate_s2dm_schema (tree , use_short_names = True )
810+
811+ # Check that empty branches are NOT in the schema type_map
812+ assert "EmptyBranch" not in schema .type_map
813+ assert "AnotherEmpty" not in schema .type_map
814+ assert "EmptyNested" not in schema .type_map
815+
816+ # Check that non-empty branches ARE in the schema
817+ assert "Vehicle" in schema .type_map
818+ assert "HasContent" in schema .type_map
819+
820+ # Generate the actual GraphQL SDL output
821+ schema_str = print_schema_with_vspec_directives (schema , unit_metadata , allowed_metadata , vspec_comments )
822+
823+ # Verify empty types don't appear in the SDL output
824+ assert "type EmptyBranch" not in schema_str
825+ assert "type AnotherEmpty" not in schema_str
826+ assert "type EmptyNested" not in schema_str
827+
828+ # Verify non-empty types DO appear in the SDL output
829+ assert "type Vehicle" in schema_str
830+ assert "type HasContent" in schema_str
831+
832+ # Verify HasContent has the expected speed field
833+ assert "speed" in schema_str .lower ()
834+
835+ # Check that warnings were logged for empty branches
836+ assert "Vehicle.EmptyBranch" in caplog .text
837+ assert "was skipped because the reference vspec does not define any sub-elements inside" in caplog .text
838+ assert "Vehicle.AnotherEmpty" in caplog .text
839+ assert "Vehicle.HasContent.EmptyNested" in caplog .text
840+
841+ def test_empty_branches_skipped_with_fqn_names (self , caplog , tmp_path ):
842+ """Test that empty branches are skipped when using FQN names (use_short_names=False)."""
843+ # Load vspec with empty branches
844+ tree , _ = get_trees (
845+ vspec = Path ("tests/vspec/test_s2dm/test_empty_branches.vspec" ),
846+ include_dirs = (),
847+ aborts = (),
848+ strict = False ,
849+ extended_attributes = (),
850+ quantities = (Path ("tests/vspec/test_s2dm/test_quantities.yaml" ),),
851+ units = (Path ("tests/vspec/test_s2dm/test_units.yaml" ),),
852+ overlays = (),
853+ expand = False ,
854+ )
855+
856+ # Generate schema with FQN names
857+ schema , unit_metadata , allowed_metadata , vspec_comments = generate_s2dm_schema (tree , use_short_names = False )
858+
859+ # Check that empty branches are NOT in the schema type_map (FQN format)
860+ assert "Vehicle_EmptyBranch" not in schema .type_map
861+ assert "Vehicle_AnotherEmpty" not in schema .type_map
862+ assert "Vehicle_HasContent_EmptyNested" not in schema .type_map
863+
864+ # Check that non-empty branches ARE in the schema
865+ assert "Vehicle" in schema .type_map
866+ assert "Vehicle_HasContent" in schema .type_map
867+
868+ # Generate the actual GraphQL SDL output
869+ schema_str = print_schema_with_vspec_directives (schema , unit_metadata , allowed_metadata , vspec_comments )
870+
871+ # Write to file for debugging
872+ output_file = tmp_path / "test_empty_branches_fqn.graphql"
873+ output_file .write_text (schema_str )
874+
875+ # Verify empty types don't appear in the SDL output (FQN format)
876+ assert "type Vehicle_EmptyBranch" not in schema_str
877+ assert "type Vehicle_AnotherEmpty" not in schema_str
878+ assert "type Vehicle_HasContent_EmptyNested" not in schema_str
879+
880+ # Verify non-empty types DO appear in the SDL output
881+ assert "type Vehicle " in schema_str or "type Vehicle {" in schema_str or "type Vehicle\n " in schema_str
882+ assert "type Vehicle_HasContent" in schema_str
883+
884+ # Check that warnings were logged for empty branches
885+ assert "Vehicle.EmptyBranch" in caplog .text
886+ assert "Vehicle.AnotherEmpty" in caplog .text
887+ assert "Vehicle.HasContent.EmptyNested" in caplog .text
888+
889+ def test_empty_branches_reported_in_not_mapped_yaml (self , tmp_path ):
890+ """Test that empty branches are reported in vspec_reference/not_mapped.yaml."""
891+ from vss_tools .exporters .s2dm import generate_vspec_reference
892+
893+ # Load vspec with empty branches
894+ tree , _ = get_trees (
895+ vspec = Path ("tests/vspec/test_s2dm/test_empty_branches.vspec" ),
896+ include_dirs = (),
897+ aborts = (),
898+ strict = False ,
899+ extended_attributes = (),
900+ quantities = (Path ("tests/vspec/test_s2dm/test_quantities.yaml" ),),
901+ units = (Path ("tests/vspec/test_s2dm/test_units.yaml" ),),
902+ overlays = (),
903+ expand = False ,
904+ )
905+
906+ # Generate schema (captures skipped branches in vspec_comments)
907+ schema , unit_metadata , allowed_metadata , vspec_comments = generate_s2dm_schema (tree , use_short_names = True )
908+
909+ # Verify that skipped branches were tracked
910+ assert "skipped_empty_branches" in vspec_comments
911+ skipped = vspec_comments ["skipped_empty_branches" ]
912+ assert len (skipped ) == 3
913+
914+ # Verify the FQNs of skipped branches
915+ skipped_fqns = [entry ["fqn" ] for entry in skipped ]
916+ assert "Vehicle.EmptyBranch" in skipped_fqns
917+ assert "Vehicle.AnotherEmpty" in skipped_fqns
918+ assert "Vehicle.HasContent.EmptyNested" in skipped_fqns
919+
920+ # Generate vspec_reference with the metadata
921+ generate_vspec_reference (
922+ tree = tree ,
923+ data_type_tree = None ,
924+ output_dir = tmp_path ,
925+ extended_attributes = (),
926+ vspec_file = Path ("tests/vspec/test_s2dm/test_empty_branches.vspec" ),
927+ units_files = (Path ("tests/vspec/test_s2dm/test_units.yaml" ),),
928+ quantities_files = (Path ("tests/vspec/test_s2dm/test_quantities.yaml" ),),
929+ mapping_metadata = vspec_comments ,
930+ )
931+
932+ # Verify not_mapped.yaml was created
933+ not_mapped_file = tmp_path / "vspec_reference" / "not_mapped.yaml"
934+ assert not_mapped_file .exists ()
935+
936+ # Verify contents of not_mapped.yaml
937+ content = not_mapped_file .read_text ()
938+ assert "skipped_empty_branches:" in content
939+ assert "Vehicle.EmptyBranch" in content
940+ assert "Vehicle.AnotherEmpty" in content
941+ assert "Vehicle.HasContent.EmptyNested" in content
942+ assert "Empty branch (no children)" in content
943+
944+ # Verify header comments are present
945+ assert "Branches Skipped During S2DM Export" in content
946+ assert "no children (no properties or sub-branches)" in content
947+
948+ # Verify README mentions the file
949+ readme_file = tmp_path / "vspec_reference" / "README.md"
950+ assert readme_file .exists ()
951+ readme_content = readme_file .read_text ()
952+ assert "not_mapped.yaml" in readme_content
953+ assert "empty branches" in readme_content .lower ()
0 commit comments