@@ -260,6 +260,67 @@ def test_non_deterministic_is_default():
260260 assert gen .deterministic is False
261261
262262
263+ def test_wl_handles_structurally_similar_bnodes ():
264+ """Blank nodes with identical local structure but different named neighbours
265+ must receive different WL signatures and thus different stable labels.
266+
267+ This tests the core WL property: two BNodes that differ only in their
268+ connected named nodes (URIs/literals) must be distinguishable.
269+ """
270+ from rdflib import BNode , Graph , Literal , Namespace , URIRef
271+
272+ from linkml .utils .generator import deterministic_turtle
273+
274+ EX = Namespace ("http://example.org/" )
275+ g = Graph ()
276+
277+ # Two restrictions with same structure but different property URIs
278+ r1 = BNode ()
279+ g .add ((r1 , URIRef ("http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ),
280+ URIRef ("http://www.w3.org/2002/07/owl#Restriction" )))
281+ g .add ((r1 , URIRef ("http://www.w3.org/2002/07/owl#onProperty" ), EX .alpha ))
282+ g .add ((r1 , URIRef ("http://www.w3.org/2002/07/owl#allValuesFrom" ), EX .Target1 ))
283+
284+ r2 = BNode ()
285+ g .add ((r2 , URIRef ("http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ),
286+ URIRef ("http://www.w3.org/2002/07/owl#Restriction" )))
287+ g .add ((r2 , URIRef ("http://www.w3.org/2002/07/owl#onProperty" ), EX .beta ))
288+ g .add ((r2 , URIRef ("http://www.w3.org/2002/07/owl#allValuesFrom" ), EX .Target2 ))
289+
290+ g .add ((EX .MyClass , URIRef ("http://www.w3.org/2000/01/rdf-schema#subClassOf" ), r1 ))
291+ g .add ((EX .MyClass , URIRef ("http://www.w3.org/2000/01/rdf-schema#subClassOf" ), r2 ))
292+
293+ # Must be deterministic across runs
294+ out1 = deterministic_turtle (g )
295+ out2 = deterministic_turtle (g )
296+ assert out1 == out2 , "WL-based serializer is not deterministic for similar BNodes"
297+
298+ # Both restrictions must appear (not collapsed)
299+ assert "alpha" in out1
300+ assert "beta" in out1
301+
302+
303+ def test_deterministic_turtle_no_bnodes ():
304+ """Graphs with no blank nodes should still produce sorted, deterministic output."""
305+ from rdflib import Graph , Literal , Namespace , URIRef
306+
307+ from linkml .utils .generator import deterministic_turtle
308+
309+ EX = Namespace ("http://example.org/" )
310+ g = Graph ()
311+ g .add ((EX .B , URIRef ("http://www.w3.org/2000/01/rdf-schema#label" ), Literal ("B" )))
312+ g .add ((EX .A , URIRef ("http://www.w3.org/2000/01/rdf-schema#label" ), Literal ("A" )))
313+
314+ out1 = deterministic_turtle (g )
315+ out2 = deterministic_turtle (g )
316+ assert out1 == out2
317+
318+ # A should appear before B (sorted)
319+ a_pos = out1 .find ("example.org/A" )
320+ b_pos = out1 .find ("example.org/B" )
321+ assert a_pos < b_pos , "Triples should be sorted: A before B"
322+
323+
263324@pytest .mark .xfail (
264325 reason = (
265326 "Collection sorting (owl:oneOf, sh:in) in deterministic mode intentionally "
0 commit comments