diff --git a/rocrate_validator/profiles/five-safes-crate/may/12_check_phase.ttl b/rocrate_validator/profiles/five-safes-crate/may/12_check_phase.ttl new file mode 100644 index 00000000..0e741f6c --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/may/12_check_phase.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 purl: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:CheckValueMayHaveStartTime + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue ; + schema:actionStatus ?status . + FILTER(?status IN ( + "http://schema.org/CompletedActionStatus", + "http://schema.org/FailedActionStatus", + "http://schema.org/ActiveActionStatus" + )) + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "StartTime" ; + sh:path schema:startTime ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:severity sh:Info ; + sh:message "`CheckValue` MAY have the `startTime` property." ; + ] . diff --git a/rocrate_validator/profiles/five-safes-crate/must/12_check_phase.ttl b/rocrate_validator/profiles/five-safes-crate/must/12_check_phase.ttl new file mode 100644 index 00000000..00cacdb5 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/12_check_phase.ttl @@ -0,0 +1,150 @@ +# 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 purl: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:CheckValueObjectHasDescriptiveNameAndIsAssessAction + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ ; + ] ; + + sh:property [ + sh:path rdf:type ; + sh:minCount 1 ; + sh:hasValue schema:AssessAction; + sh:severity sh:Violation ; + sh:message "CheckValue MUST be a `schema:AssessAction`." ; + ] ; + + sh:property [ + sh:a sh:PropertyShape ; + sh:name "name" ; + sh:description "CheckValue MUST have a human readable name string of at least 20 characters." ; + sh:path schema:name ; + sh:datatype xsd:string ; + sh:minLength 20 ; + sh:severity sh:Violation ; + sh:message "CheckValue MUST have a human readable name string of at least 20 characters." ; + ] . + +five-safes-crate:CheckValueStartTimeMustFollowISOStandard + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue ; + schema:startTime ?o + + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "StartTime" ; + sh:path schema:startTime ; + sh:pattern "^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt][0-9]{2}:[0-9]{2}:[0-9]{2}([.|,][0-9]+)?(Z|z|[+-][0-9]{2}:[0-9]{2})$" ; + sh:severity sh:Violation ; + sh:message "`CheckValue` --> `startTime` MUST follows the RFC 3339 standard (YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." ; + ] . + + +five-safes-crate:CheckValueEndTimeMustFollowISOStandard + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue ; + schema:endTime ?o + + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "EndTime" ; + sh:path schema:endTime ; + sh:pattern "^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt][0-9]{2}:[0-9]{2}:[0-9]{2}([.|,][0-9]+)?(Z|z|[+-][0-9]{2}:[0-9]{2})$" ; + sh:severity sh:Violation ; + sh:message "`CheckValue` --> `endTime` MUST follows the RFC 3339 standard (YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." ; + ] . + + +five-safes-crate:CheckValueActionStatusMustHaveAllowedValues + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue ; + schema:actionStatus ?status . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "ActionStatus" ; + sh:path schema:actionStatus ; + sh:in ( + "http://schema.org/PotentialActionStatus" + "http://schema.org/ActiveActionStatus" + "http://schema.org/CompletedActionStatus" + "http://schema.org/FailedActionStatus" + ) ; + sh:severity sh:Violation ; + sh:message "`CheckValue` --> `actionStatus` MUST have one of the allowed values." ; + ] . diff --git a/rocrate_validator/profiles/five-safes-crate/should/12_check_phase.ttl b/rocrate_validator/profiles/five-safes-crate/should/12_check_phase.ttl new file mode 100644 index 00000000..0560cc15 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/should/12_check_phase.ttl @@ -0,0 +1,191 @@ +# 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 purl: . +@prefix sh: . +@prefix validator: . +@prefix xsd: . + + +five-safes-crate:RootDataEntityShouldMentionCheckValueObject + a sh:NodeShape ; + sh:name "RootDataEntity" ; + sh:targetClass ro-crate:RootDataEntity ; + sh:description "" ; + + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "mentions" ; + sh:description "RootDataEntity SHOULD mention a check value object." ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT $this + WHERE { + FILTER NOT EXISTS{ + $this schema:mentions ?action . + ?action schema:additionalType shp:CheckValue . + } + } + """ ; + sh:severity sh:Warning ; + sh:message "RootDataEntity SHOULD mention a check value object." ; + ] . + + +five-safes-crate:CheckValueObjectShouldPointToRootDataEntity + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "object" ; + sh:path schema:object ; + sh:minCount 1 ; + sh:hasValue <./> ; + sh:severity sh:Warning ; + sh:message "`CheckValue` --> `object` SHOULD point to the root of the RO-Crate" ; + ] . + + +five-safes-crate:CheckValueInstrumentShouldPointToEntityTypedDefinedTerm + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "instrument" ; + sh:path schema:instrument ; + sh:minCount 1 ; + sh:class schema:DefinedTerm ; + sh:severity sh:Warning ; + sh:message "`CheckValue` --> `instrument` SHOULD point to an entity typed `schema:DefinedTerm`" ; + ] . + + +five-safes-crate:CheckValueAgentShouldIdentifyTheAgentWhoPerformnedTheCheck + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "agent" ; + sh:path schema:agent ; + sh:minCount 1 ; + sh:nodeKind sh:IRI ; + sh:severity sh:Warning ; + sh:message "`CheckValue` --> `agent` SHOULD reference the agent who initiated the check" ; + ] . + + +five-safes-crate:CheckValueShouldHaveEndTime + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue ; + schema:actionStatus ?status . + FILTER(?status IN ( + "http://schema.org/CompletedActionStatus", + "http://schema.org/FailedActionStatus" + )) + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "EndTime" ; + sh:path schema:endTime ; + sh:minCount 1 ; + sh:maxCount 1 ; + sh:severity sh:Warning ; + sh:message "`CheckValue` SHOULD have the `endTime` property." ; + ] . + + +five-safes-crate:CheckValueShouldHaveActionStatus + a sh:NodeShape ; + sh:name "CheckValue" ; + sh:description "" ; + + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "ActionStatus" ; + sh:path schema:actionStatus ; + sh:minCount 1 ; + sh:severity sh:Warning ; + sh:message "CheckValue SHOULD have actionStatus property." ; + ] . + 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 c5c50961..aa9b3adf 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 @@ -220,6 +220,7 @@ "@id": "https://w3id.org/shp#CheckValue" }, "name": "BagIt checksum of Crate: OK", + "startTime": "2023-04-18T12:11:00+01:00", "endTime": "2023-04-18T12:11:45+01:00", "object": { "@id": "./" diff --git a/tests/integration/profiles/five-safes-crate/test_5src_12_check_phase.py b/tests/integration/profiles/five-safes-crate/test_5src_12_check_phase.py new file mode 100644 index 00000000..5cc974cc --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_12_check_phase.py @@ -0,0 +1,395 @@ +# 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 + +# set up logging +logger = logging.getLogger(__name__) + + +# ----- MUST fails tests + +# TO BE CHECKED AGAIN +def test_5src_check_value_not_of_type_assess_action(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?this rdf:type ?type . + } + INSERT { + ?this rdf:type . + } + WHERE { + ?this a schema:AssessAction ; + schema:additionalType shp:CheckValue ; + rdf:type ?type . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["CheckValue MUST be a `schema:AssessAction`."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_name_not_a_string(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?this schema:name ?name . + } + INSERT { + ?this schema:name 123 . + } + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["CheckValue MUST have a human readable name string of at least 20 characters."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_name_not_long_enough(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?this schema:name ?name . + } + INSERT { + ?this schema:name "Short" . + } + WHERE { + ?this schema:additionalType shp:CheckValue . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["CheckValue MUST have a human readable name string of at least 20 characters."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_start_time_not_iso_standard(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?c schema:startTime ?t . + } + INSERT { + ?c schema:startTime "1st of Jan 2021" . + } + WHERE { + ?c schema:additionalType shp:CheckValue ; + schema:startTime ?t . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=[( + "`CheckValue` --> `startTime` MUST follows the RFC 3339 standard " + "(YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." + )], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_end_time_not_iso_standard(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?c schema:endTime ?t . + } + INSERT { + ?c schema:endTime "1st of Jan 2021" . + } + WHERE { + ?c schema:additionalType shp:CheckValue ; + schema:endTime ?t . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=[( + "`CheckValue` --> `endTime` MUST follows the RFC 3339 standard " + "(YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." + )], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_has_action_status_with_not_allowed_value(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?s schema:actionStatus ?o . + } + INSERT { + ?s schema:actionStatus "Not a good action status" . + } + WHERE { + ?s schema:additionalType ; + schema:actionStatus ?o . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["`CheckValue` --> `actionStatus` MUST have one of the allowed values."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ----- SHOULD fails tests + +def test_5src_root_data_entity_does_not_mention_check_value_entity(): + sparql = """ + PREFIX schema: + PREFIX shp: + + DELETE { + <./> schema:mentions ?o . + } + WHERE { + ?o schema:additionalType shp:CheckValue ; + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["RootDataEntity"], + expected_triggered_issues=["RootDataEntity SHOULD mention a check value object."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_object_does_not_point_to_root_data_entity(): + sparql = """ + PREFIX schema: + PREFIX shp: + + DELETE { + ?s schema:object <./> . + } + INSERT { + ?s schema:object "not the RootDataEntity" . + } + WHERE { + ?s schema:additionalType shp:CheckValue ; + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["`CheckValue` --> `object` SHOULD point to the root of the RO-Crate"], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_instrument_does_not_point_to_entity_with_type_defined_term(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?s rdf:type schema:DefinedTerm . + } + INSERT { + ?s rdf:type schema:Persona . + } + WHERE { + ?cv schema:additionalType shp:CheckValue ; + schema:instrument ?s . + ?s rdf:type schema:DefinedTerm . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=[ + "`CheckValue` --> `instrument` SHOULD point to an entity typed `schema:DefinedTerm`" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_does_not_have_end_time(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?c schema:endTime ?t . + } + WHERE { + ?c schema:additionalType shp:CheckValue ; + schema:endTime ?t . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["`CheckValue` SHOULD have the `endTime` property."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_does_not_have_action_status_property(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?s schema:actionStatus ?o . + } + WHERE { + ?s schema:additionalType shp:CheckValue ; + schema:actionStatus ?o . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["CheckValue SHOULD have actionStatus property."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_check_value_does_not_point_to_an_agent(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?s schema:agent ?o . + } + WHERE { + ?s schema:additionalType shp:CheckValue ; + schema:agent ?o . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["`CheckValue` --> `agent` SHOULD reference the agent who initiated the check"], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ----- MAY fails tests + +def test_5src_check_value_does_not_have_start_time(): + sparql = """ + PREFIX schema: + PREFIX shp: + PREFIX rdf: + + DELETE { + ?c schema:startTime ?t . + } + WHERE { + ?c schema:additionalType shp:CheckValue ; + schema:startTime ?t . + } + """ + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, + expected_triggered_requirements=["CheckValue"], + expected_triggered_issues=["`CheckValue` MAY have the `startTime` property."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + )