diff --git a/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl new file mode 100644 index 000000000..5dd46fd27 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/may/1_responsible_project.ttl @@ -0,0 +1,56 @@ +# Copyright (c) 2025 eScience Lab, The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProject + a sh:NodeShape ; + sh:name "Responsible Project" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "funding" ; + sh:path schema:funding; + sh:minCount 1 ; + sh:severity sh:Info ; + sh:message """The Responsible Project does not have the property `funding`.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "member" ; + sh:path schema:member; + sh:minCount 1 ; + sh:severity sh:Info ; + sh:message """The Responsible Project does not have the property `member`.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl new file mode 100644 index 000000000..2cc0d434d --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/1_requesting_agent.ttl @@ -0,0 +1,82 @@ +# Copyright (c) 2025 eScience Lab, The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:AgentIsMemberOf + a sh:NodeShape ; + sh:name "Requesting Agent" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "memberOf" ; + sh:path schema:memberOf; + sh:class schema:Project ; + sh:severity sh:Violation ; + sh:message """The 'memberOf' property of an agent MUST be of type Project.""" ; + ] . + + +five-safes-crate:AgentProjectIntersection + a sh:NodeShape ; + sh:name "Agent Project Intersection" ; + sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "Agent Project Intersection" ; + sh:description """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT $this WHERE { + FILTER EXISTS { + $this schema:memberOf ?anyProject . + } + FILTER NOT EXISTS { + $this schema:memberOf ?commonProject . + ?metadata schema:about ?root . + ?root schema:sourceOrganization ?commonProject . + } + } + """ ; + sh:severity sh:Violation ; + sh:message """At least one Project referenced by Agent -> memberOf MUST be included in the set of Projects referenced by RootDataEntity -> sourceOrganization.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl new file mode 100644 index 000000000..c06c873ea --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/1_responsible_project.ttl @@ -0,0 +1,59 @@ +# Copyright (c) 2025 eScience Lab, The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProject + a sh:NodeShape ; + sh:name "Responsible Project" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?this . + } + """ + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "funding" ; + sh:path schema:funding; + sh:class schema:Grant ; + sh:severity sh:Violation ; + sh:message """The property 'funding' of the Responsible Project MUST be of type schema:Grant.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "member" ; + sh:path schema:member; + sh:or ( + [ sh:class schema:Organization ] + [ sh:class schema:Person ] + ) ; + sh:severity sh:Violation ; + sh:message """The property 'member' of the Responsible Project MUST be of type schema:Organization or schema:Person.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl similarity index 65% rename from rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl rename to rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl index 1145bb5a3..bb9a51471 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity_metadata.ttl +++ b/rocrate_validator/profiles/five-safes-crate/must/1_root_data_entity.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,23 +16,31 @@ @prefix ro-crate: . @prefix five-safes-crate: . @prefix rdf: . -@prefix schema_org: . +@prefix schema: . @prefix sh: . @prefix validator: . @prefix xsd: . + five-safes-crate:RootDataEntityRequiredProperties a sh:NodeShape ; - sh:name "Five Safes Crate Root Data Entity REQUIRED properties" ; - sh:description "The Root Data Entity MUST have a `sourceOrganisation`" ; + sh:name "RootDataEntity" ; sh:targetClass ro-crate:RootDataEntity ; + sh:property [ a sh:PropertyShape ; - sh:name "Root Data Entity: `sourceOrganization` property" ; - sh:description """Check if the Root Data Entity includes a `sourceOrganization` (as specified by schema.org).""" ; - sh:path schema_org:sourceOrganization; + sh:name "sourceOrganization" ; + sh:path schema:sourceOrganization; sh:minCount 1 ; - sh:nodeKind sh:IRI; - sh:message """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" ; + sh:severity sh:Violation ; + sh:message """The Root Data Entity MUST have a `sourceOrganization` property.""" ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "sourceOrganization" ; + sh:path schema:sourceOrganization ; + sh:class schema:Project ; + sh:severity sh:Violation ; + sh:message """The `sourceOrganization` property of the RootDataEntity MUST point to a Project entity.""" ; ] . diff --git a/rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl similarity index 51% rename from rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl rename to rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl index 474fda436..332c67d3d 100644 --- a/rocrate_validator/profiles/five-safes-crate/must/1_funder.ttl +++ b/rocrate_validator/profiles/five-safes-crate/should/1_requesting_agent.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2024-2025 CRS4 +# Copyright (c) 2025 eScience Lab, The University of Manchester # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,22 +15,32 @@ @prefix ro: <./> . @prefix ro-crate: . @prefix five-safes-crate: . -@prefix schema_org: . +@prefix rdf: . +@prefix schema: . @prefix sh: . -@prefix bioschemas: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:AgentIsMemberOf + a sh:NodeShape ; + sh:name "Requesting Agent" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + } + """ + ] ; -# TODO: note that this applies to all Projects, not just those targeted by `funding` relationships -five-safes-crate:FundingBody a sh:NodeShape ; - sh:name "Funding body Project" ; - sh:description "Project which is funding this work" ; - sh:targetClass schema_org:Project ; sh:property [ a sh:PropertyShape ; - sh:name "Project Name" ; - sh:description """Check if the Project Entity `name` (as specified by schema.org) - to clearly identify the dataset and distinguish it from other projects.""" ; + sh:name "memberOf" ; + sh:path schema:memberOf; sh:minCount 1 ; - sh:nodeKind sh:Literal ; - sh:path schema_org:name; - sh:message "The Project Entity MUST have a `name` property (as specified by schema.org)" ; - ] . + sh:severity sh:Warning ; + sh:message """The Requesting Agent SHOULD have a `memberOf` property.""" ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl new file mode 100644 index 000000000..28d1c1eef --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/should/1_responsible_project.ttl @@ -0,0 +1,60 @@ +# Copyright (c) 2025 eScience Lab, The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +@prefix ro: <./> . +@prefix ro-crate: . +@prefix five-safes-crate: . +@prefix rdf: . +@prefix schema: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:ResponsibleProjectMemberAndSourceOrganizationIntersection + a sh:NodeShape ; + sh:name "Organizations (members of Responsible Project)" ; + sh:description """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; + sh:target [ + a sh:SPARQLTarget ; + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT DISTINCT ?this WHERE { + ?action a schema:CreateAction ; + schema:agent ?this . + ?this a schema:Person ; + schema:memberOf ?project ; + schema:affiliation ?someAffiliation . + ?project schema:member ?org2 . + } + """ + ] ; + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "Intersection with agent affiliations" ; + sh:description """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; + + sh:prefixes ro-crate:sparqlPrefixes ; + sh:select """ + SELECT $this WHERE { + FILTER NOT EXISTS { + $this schema:affiliation ?org . + $this schema:memberOf ?project . + ?project schema:member ?org . + } + } + """ ; + sh:severity sh:Warning ; + sh:message """At least one of the organisations that are members of the responsible project SHOULD be included in the Requesting Agent's affiliations, if such properties exist.""" ; + ] . \ No newline at end of file diff --git a/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json b/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json index 9fb0d7bf5..732854019 100644 --- a/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json +++ b/tests/data/crates/valid/five-safes-crate-request/ro-crate-metadata.json @@ -101,6 +101,11 @@ "@type": "Organization", "name": "The University of Manchester" }, + { + "@id": "https://ror.org/01ee9ar58", + "@type": "Organization", + "name": "University of Nottingham" + }, { "@id": "#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70", "@type": "Project", diff --git a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json index a6475f63c..f1f48cd99 100644 --- a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json +++ b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json @@ -366,6 +366,11 @@ "@type": "Organization", "name": "The University of Manchester" }, + { + "@id": "https://ror.org/01ee9ar58", + "@type": "Organization", + "name": "University of Nottingham" + }, { "@id": "https://gtr.ukri.org/projects?ref=10038961", "@type": "Grant", diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py new file mode 100644 index 000000000..9601f6fa3 --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_requesting_agent.py @@ -0,0 +1,138 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ----- MUST fails tests + + +def test_5src_agent_memberOf_not_project(): + """ + Test a Five Safes Crate where an agent's `memberOf` does NOT reference a schema:Project. + (We replace the referenced Project with a plain literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + INSERT { + ?agent schema:memberOf "Not a project (literal replacement)" + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?org . + ?org a schema:Project . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Requesting Agent"], + expected_triggered_issues=[ + "The 'memberOf' property of an agent MUST be of type Project." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_agent_memberOf_project_not_in_root(): + """ + Test a Five Safes Crate where NONE of the Projects referenced by Agent->memberOf are included + in the set of Projects referenced by RootDataEntity->sourceOrganization. + (We replace an agent's memberOf with a new Project that the root does not reference.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + INSERT { + # assign the agent to a new Project that is not referenced by the Root Data Entity + ?agent schema:memberOf <#missing-project> . + <#missing-project> a schema:Project . + } + WHERE { + # locate a CreateAction -> agent -> memberOf that currently points to a Project + ?action a schema:CreateAction ; + schema:agent ?agent . + + ?agent schema:memberOf ?org . + ?org a schema:Project . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Agent Project Intersection"], + expected_triggered_issues=[ + ( + "At least one Project referenced by Agent -> memberOf MUST be included " + "in the set of Projects referenced by RootDataEntity -> sourceOrganization." + ) + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ----- SHOULD warns tests +def test_5src_agent_memberOf_missing_warning(): + """ + Test a Five Safes Crate where the Requesting Agent does NOT have the 'memberOf' property. + This should trigger the SHACL warning: the Requesting Agent SHOULD have a `memberOf` property. + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:memberOf ?org . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?org . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, # or True if warnings are not treated as failures + expected_triggered_requirements=["Requesting Agent"], + expected_triggered_issues=[ + "The Requesting Agent SHOULD have a `memberOf` property." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py new file mode 100644 index 000000000..daa8d90ac --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_responsible_project.py @@ -0,0 +1,218 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ---- MUST fails tests + + +def test_5src_responsible_project_funding_not_grant(): + """ + Test a Five Safes Crate where a Responsible Project's `funding` property + is NOT of type schema:Grant. + (We replace the funding reference with a literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:funding ?grant . + } + INSERT { + ?project schema:funding "Not a grant (literal replacement)" . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:funding ?grant . + ?grant a schema:Grant . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The property 'funding' of the Responsible Project MUST be of type schema:Grant." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_responsible_project_member_not_organization(): + """ + Test a Five Safes Crate where a Responsible Project's `member` property + is NOT of type schema:Organization. + (We replace the member reference with a literal.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:member ?org . + } + INSERT { + ?project schema:member "Not organization or person (literal replacement)" . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:member ?org . + ?org a schema:Organization . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The property 'member' of the Responsible Project MUST be of type schema:Organization or schema:Person." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ---- SHOULD warns tests + + +def test_5src_responsible_project_member_and_agent_affiliation_no_intersection(): + """ + Test a Five Safes Crate where none of the organisations that are members of the + Responsible Project appear in the Requesting Agent's affiliations (violates the + 'Intersection with agent affiliations' SHACL warning). + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?agent schema:affiliation ?oldAff . + } + INSERT { + ?agent schema:affiliation <#missing-affiliation> . + <#missing-affiliation> a schema:Organization . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent a schema:Person ; + schema:memberOf ?project ; + schema:affiliation ?oldAff . + ?project schema:member ?oldAff . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=[ + "Organizations (members of Responsible Project)" + ], + expected_triggered_issues=[ + ( + "At least one of the organisations that are members of the responsible project SHOULD " + "be included in the Requesting Agent's affiliations, if such properties exist." + ) + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ---- MAY warns tests + + +def test_5src_responsible_project_missing_funding_property(): + """ + Test a Five Safes Crate where a Responsible Project does NOT have the `funding` property. + This should trigger the SHACL info: 'The Responsible Project does not have the property `funding`.' + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:funding ?f . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:funding ?f . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The Responsible Project does not have the property `funding`." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_responsible_project_missing_member_property(): + """ + Test a Five Safes Crate where a Responsible Project does NOT have the `member` property. + This should trigger the SHACL info: 'The Responsible Project does not have the property `member`.' + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?project schema:member ?m . + } + WHERE { + ?action a schema:CreateAction ; + schema:agent ?agent . + ?agent schema:memberOf ?project . + ?project schema:member ?m . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, + expected_triggered_requirements=["Responsible Project"], + expected_triggered_issues=[ + "The Responsible Project does not have the property `member`." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py b/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py new file mode 100644 index 000000000..54ddce7ee --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_1_root_data_entity_metadata.py @@ -0,0 +1,89 @@ +# Copyright (c) 2024-2025 CRS4 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from rocrate_validator.models import Severity +from tests.ro_crates import ValidROC +from tests.shared import do_entity_test, SPARQL_PREFIXES + +# set up logging +logger = logging.getLogger(__name__) + + +# ----- MUST fails tests + + +def test_5src_root_data_entity_without_source_organization(): + """ + Test a Five Safes Crate where the Root Data Entity does NOT have the 'sourceOrganization' property. + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + <./> schema:sourceOrganization ?o . + } + WHERE { + <./> schema:sourceOrganization ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["RootDataEntity"], + expected_triggered_issues=[ + """The Root Data Entity MUST have a `sourceOrganization` property.""" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_root_data_entity_source_organization_not_organization(): + """ + Test a Five Safes Crate where the Root Data Entity's `sourceOrganization` property + does NOT reference a `schema:Project` entity. + (We replace any existing sourceOrganization with a literal to violate the class constraint.) + """ + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + <./> schema:sourceOrganization ?o . + } + INSERT { + # insert a literal instead of an IRI + <./> schema:sourceOrganization "Investigation of cancer (TRE72 project 81)" . + } + WHERE { + <./> schema:sourceOrganization ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_request, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["RootDataEntity"], + expected_triggered_issues=[ + """The `sourceOrganization` property of the RootDataEntity MUST point to a Project entity.""" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_funding.py b/tests/integration/profiles/five-safes-crate/test_5src_funding.py deleted file mode 100644 index 58b2f5746..000000000 --- a/tests/integration/profiles/five-safes-crate/test_5src_funding.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from rocrate_validator.models import Severity -from tests.ro_crates import ValidROC -from tests.shared import do_entity_test, SPARQL_PREFIXES - -# set up logging -logger = logging.getLogger(__name__) - - -def test_5src_funding_project_no_name(): - """\ - Test a Five Safes Crate where the funding Project does not have a name. - """ - sparql = SPARQL_PREFIXES + """DELETE WHERE { - <#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70> schema:name "Investigation of cancer (TRE72 project 81)" -} -""" - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Funding body Project"], - expected_triggered_issues=[ - "The Project Entity MUST have a `name` property (as specified by schema.org)" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - ) diff --git a/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py b/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py deleted file mode 100644 index 92417caa7..000000000 --- a/tests/integration/profiles/five-safes-crate/test_5src_root_data_entity_metadata.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2024-2025 CRS4 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging - -from rocrate_validator.models import Severity -from tests.ro_crates import ValidROC -from tests.shared import do_entity_test, SPARQL_PREFIXES - -# set up logging -logger = logging.getLogger(__name__) - - -def test_5src_root_data_entity_no_source_organization(): - """\ - Test a Five Safes Crate where the Root Data Entity it does not reference a sourceOrganization. - """ - sparql = SPARQL_PREFIXES + """DELETE WHERE { - <./> schema:sourceOrganization ?object - } - """ - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Five Safes Crate Root Data Entity REQUIRED properties"], - expected_triggered_issues=[ - """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - ) - - -def test_5src_root_data_entity_source_organization_not_entity(): - """\ - Test a Five Safes Crate where the Root Data Entity it does not reference a sourceOrganization. - """ - sparql = SPARQL_PREFIXES + """DELETE { - <./> schema:sourceOrganization ?o - } - INSERT { - <./> schema:sourceOrganization "Investigation of cancer (TRE72 project 81)" - } - WHERE { - <./> schema:sourceOrganization ?o - } - -""" - - do_entity_test( - rocrate_path=ValidROC().five_safes_crate_result, - requirement_severity=Severity.REQUIRED, - expected_validation_result=False, - expected_triggered_requirements=["Five Safes Crate Root Data Entity REQUIRED properties"], - expected_triggered_issues=[ - """The Root Data Entity MUST have a `sourceOrganization` property (as specified by schema.org). - SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name.""" - ], - profile_identifier="five-safes-crate", - rocrate_entity_mod_sparql=sparql, - )