1+ import copy
12import os
23import pytest
34
@@ -321,7 +322,7 @@ def test_create_new_instance(kg_client, mocker):
321322 )
322323 fake_id = "00000000-0000-0000-0000-000000000000"
323324 response = kg_client .create_new_instance ({"a" : 1 , "b" : 2 }, instance_id = fake_id , space = "not-a-real-space" )
324- assert response == {"@id" : fake_id , "a" : 1 , "b" : 2 }
325+ assert response == {"@id" : kg_client . uri_from_uuid ( fake_id ) , "a" : 1 , "b" : 2 }
325326
326327
327328@skip_if_no_connection
@@ -333,7 +334,7 @@ def test_replace_instance(kg_client, mocker):
333334 )
334335 fake_id = "00000000-0000-0000-0000-000000000000"
335336 response = kg_client .replace_instance (fake_id , {"a" : 1 , "b" : 2 })
336- assert response == {"@id" : fake_id , "a" : 1 , "b" : 2 }
337+ assert response == {"@id" : kg_client . uri_from_uuid ( fake_id ) , "a" : 1 , "b" : 2 }
337338
338339
339340@skip_if_no_connection
@@ -553,3 +554,145 @@ def test_clean_space_lists_both_kinds_then_aborts(self, mock_client, mocker, cap
553554 out = capsys .readouterr ().out
554555 assert "Person 3" in out
555556 assert f"{ self .unknown_type } 7" in out
557+
558+
559+ class TestBareUuidLinks :
560+ """Marmotgraph v4 gives links to other KG instances as bare UUIDs rather than full URIs.
561+ The client expands them, so that links can be resolved and compared with instance ids."""
562+
563+ namespace = "https://kg.ebrains.eu/api/instances/"
564+ dsv_uuid = "00000000-0000-0000-0000-000000000001"
565+ target_uuid = "00000000-0000-0000-0000-00000000000a"
566+
567+ def dataset_version (self , id_ ):
568+ return {
569+ "@id" : id_ ,
570+ "@type" : ["https://openminds.om-i.org/types/DatasetVersion" ],
571+ "http://schema.org/identifier" : [self .dsv_uuid , self .namespace + self .dsv_uuid ],
572+ "https://core.kg.ebrains.eu/vocab/meta/space" : "dataset" ,
573+ "https://openminds.om-i.org/props/accessibility" : {"@id" : self .target_uuid },
574+ "https://openminds.om-i.org/props/digitalIdentifier" : {"@id" : "https://doi.org/10.25493/6640-3XH" },
575+ "https://openminds.om-i.org/props/technique" : [
576+ {"@id" : "https://openminds.om-i.org/instances/technique/spatialRegistration" },
577+ {"@id" : self .target_uuid .upper ()},
578+ ],
579+ }
580+
581+ def test_expand_bare_uuids (self ):
582+ from fairgraph .client import expand_bare_uuids
583+
584+ data = [
585+ {
586+ "@id" : self .dsv_uuid ,
587+ "http://schema.org/identifier" : [self .dsv_uuid ],
588+ "https://openminds.om-i.org/props/link" : {"@id" : self .target_uuid },
589+ "https://openminds.om-i.org/props/links" : [
590+ {"@id" : self .target_uuid },
591+ {"@id" : "https://example.com/x" },
592+ ],
593+ "https://openminds.om-i.org/props/embedded" : {
594+ "@id" : f"{ self .dsv_uuid } _emb_1" ,
595+ "https://openminds.om-i.org/props/nested" : {"@id" : self .target_uuid },
596+ },
597+ "https://openminds.om-i.org/props/name" : self .target_uuid ,
598+ }
599+ ]
600+ result = expand_bare_uuids (data , self .namespace )
601+ assert result is data
602+ assert data == [
603+ {
604+ "@id" : self .namespace + self .dsv_uuid ,
605+ "http://schema.org/identifier" : [self .dsv_uuid ], # not an "@id", so unchanged
606+ "https://openminds.om-i.org/props/link" : {"@id" : self .namespace + self .target_uuid },
607+ "https://openminds.om-i.org/props/links" : [
608+ {"@id" : self .namespace + self .target_uuid },
609+ {"@id" : "https://example.com/x" },
610+ ],
611+ "https://openminds.om-i.org/props/embedded" : {
612+ "@id" : f"{ self .dsv_uuid } _emb_1" ,
613+ "https://openminds.om-i.org/props/nested" : {"@id" : self .namespace + self .target_uuid },
614+ },
615+ "https://openminds.om-i.org/props/name" : self .target_uuid ,
616+ }
617+ ]
618+
619+ def test_full_uris_unchanged (self ):
620+ from fairgraph .client import expand_bare_uuids
621+
622+ data = self .dataset_version (self .namespace + self .dsv_uuid )
623+ for item in data ["https://openminds.om-i.org/props/technique" ]:
624+ item ["@id" ] = "https://openminds.om-i.org/instances/technique/spatialRegistration"
625+ data ["https://openminds.om-i.org/props/accessibility" ]["@id" ] = self .namespace + self .target_uuid
626+ expected = copy .deepcopy (data )
627+ assert expand_bare_uuids (data , self .namespace ) == expected
628+
629+ def test_list (self , offline_kg_client , mocker ):
630+ mocker .patch .object (
631+ offline_kg_client ._kg_client .instances ,
632+ "list" ,
633+ lambda ** kw : MockKGResponse ([self .dataset_version (self .namespace + self .dsv_uuid )]),
634+ )
635+ data = offline_kg_client .list ("https://openminds.om-i.org/types/DatasetVersion" ).data [0 ]
636+ assert data ["https://openminds.om-i.org/props/accessibility" ] == {"@id" : self .namespace + self .target_uuid }
637+ assert data ["https://openminds.om-i.org/props/technique" ] == [
638+ {"@id" : "https://openminds.om-i.org/instances/technique/spatialRegistration" },
639+ {"@id" : self .namespace + self .target_uuid .upper ()},
640+ ]
641+ assert data ["https://openminds.om-i.org/props/digitalIdentifier" ] == {
642+ "@id" : "https://doi.org/10.25493/6640-3XH"
643+ }
644+
645+ def test_query (self , offline_kg_client , mocker ):
646+ mocker .patch .object (
647+ offline_kg_client ._kg_client .queries ,
648+ "test_query" ,
649+ lambda * args , ** kw : MockKGResponse ([{"@id" : self .dsv_uuid , "accessibility" : {"@id" : self .target_uuid }}]),
650+ )
651+ data = offline_kg_client .query ({"@context" : {}, "structure" : []}).data
652+ assert data == [
653+ {"@id" : self .namespace + self .dsv_uuid , "accessibility" : {"@id" : self .namespace + self .target_uuid }}
654+ ]
655+
656+ def test_resolve_link (self , offline_kg_client , mocker ):
657+ from fairgraph .openminds .core import DatasetVersion
658+ from fairgraph .openminds .controlled_terms import ProductAccessibility
659+
660+ server = {
661+ self .dsv_uuid : self .dataset_version (self .namespace + self .dsv_uuid ),
662+ self .target_uuid : {
663+ "@id" : self .namespace + self .target_uuid ,
664+ "@type" : ["https://openminds.om-i.org/types/ProductAccessibility" ],
665+ "http://schema.org/identifier" : [self .namespace + self .target_uuid ],
666+ "https://core.kg.ebrains.eu/vocab/meta/space" : "controlled" ,
667+ "https://openminds.om-i.org/props/name" : "controlled access" ,
668+ },
669+ }
670+ mocker .patch .object (
671+ offline_kg_client ._kg_client .instances ,
672+ "get_by_id" ,
673+ lambda stage , instance_id , extended_response_configuration : MockKGResponse (
674+ copy .deepcopy (server [str (instance_id )])
675+ ),
676+ )
677+ dsv = DatasetVersion .from_id (self .dsv_uuid , offline_kg_client )
678+ assert dsv .accessibility .id == self .namespace + self .target_uuid
679+ accessibility = dsv .accessibility .resolve (offline_kg_client )
680+ assert isinstance (accessibility , ProductAccessibility )
681+ assert accessibility .name == "controlled access"
682+ assert accessibility .id == dsv .accessibility .id
683+
684+ def test_openminds_instance (self , offline_kg_client , mocker ):
685+ uri = "https://openminds.om-i.org/instances/technique/spatialRegistration"
686+ result = mocker .Mock (
687+ data = {"@id" : self .target_uuid , "https://openminds.om-i.org/props/link" : {"@id" : self .dsv_uuid }}
688+ )
689+ mocker .patch .object (
690+ offline_kg_client ._kg_client .instances ,
691+ "get_by_identifiers" ,
692+ lambda ** kw : mocker .Mock (data = {uri : result }),
693+ )
694+ data = offline_kg_client .instance_from_full_uri (uri , use_cache = False , require_full_data = False )
695+ assert data == {
696+ "@id" : self .namespace + self .target_uuid ,
697+ "https://openminds.om-i.org/props/link" : {"@id" : self .namespace + self .dsv_uuid },
698+ }
0 commit comments