diff --git a/rocrate_validator/profiles/five-safes-crate/may/13_validation_phase.ttl b/rocrate_validator/profiles/five-safes-crate/may/13_validation_phase.ttl new file mode 100644 index 00000000..b7adcb3b --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/may/13_validation_phase.ttl @@ -0,0 +1,57 @@ +# 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:DownloadActionMayHaveStartTimeIfBegun + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck ; + 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:description "ValidationCheck MAY have the `startTime` property if `actionStatus` is either ActiveActionStatus, CompletedActionStatus or FailedActionStatus." ; + sh:message "ValidationCheck MAY have the `startTime` property if `actionStatus` is either ActiveActionStatus, CompletedActionStatus or FailedActionStatus." ; + ] . diff --git a/rocrate_validator/profiles/five-safes-crate/must/13_validation_phase.ttl b/rocrate_validator/profiles/five-safes-crate/must/13_validation_phase.ttl new file mode 100644 index 00000000..610d0b45 --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/must/13_validation_phase.ttl @@ -0,0 +1,182 @@ +# 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:ValidationCheckObjectHasDescriptiveNameAndIsAssessAction + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + sh:path rdf:type ; + sh:minCount 1 ; + sh:hasValue schema:AssessAction; + sh:severity sh:Violation ; + sh:message "ValidationCheck MUST be a `schema:AssessAction`." ; + ] ; + + sh:property [ + sh:a sh:PropertyShape ; + sh:name "name" ; + sh:description "ValidationCheck MUST have a human readable name string." ; + sh:path schema:name ; + sh:datatype xsd:string ; + sh:severity sh:Violation ; + sh:message "ValidationCheck MUST have a human readable name string." ; + ] . + + +five-safes-crate:ValidationCheckActionStatusMustHaveAllowedValue + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck ; + schema:actionStatus ?status . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "actionStatus" ; + sh:description "The `actionStatus` of ValidationCheck MUST have an allowed value (see https://schema.org/ActionStatusType)." ; + 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 "The `actionStatus` of ValidationCheck MUST have an allowed value (see https://schema.org/ActionStatusType)." ; + ] . + + +five-safes-crate:ValidationCheckActionStatusMustHaveAllowedValue + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:minCount 1 ; + sh:name "actionStatus" ; + sh:description "actionStatus MUST be either PotentialActionStatus, ActiveActionStatus, CompletedActionStatus, or FailedActionStatus." ; + 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 "actionStatus MUST be either PotentialActionStatus, ActiveActionStatus, CompletedActionStatus, or FailedActionStatus." ; + ] . + + +five-safes-crate:ValidationCheckStartTimeMUSTFollowISOStandard + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "StartTime" ; + sh:minCount 0; + 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 "ValidationCheck --> `startTime` MUST follows the RFC 3339 standard (YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." ; + ] . + + +five-safes-crate:ValidationCheckEndTimeMUSTFollowISOStandard + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "EndTime" ; + sh:minCount 0; + 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 "ValidationCheck --> `endTime` MUST follows the RFC 3339 standard (YYYY-MM-DD'T'hh:mm:ss[.fraction](Z | ±hh:mm))." ; + ] . \ No newline at end of file diff --git a/rocrate_validator/profiles/five-safes-crate/should/13_validation_phase.ttl b/rocrate_validator/profiles/five-safes-crate/should/13_validation_phase.ttl new file mode 100644 index 00000000..6558715f --- /dev/null +++ b/rocrate_validator/profiles/five-safes-crate/should/13_validation_phase.ttl @@ -0,0 +1,173 @@ +# 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:RootDataEntityShouldMentionValidationCheckObject + 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 ValidationCheck object." ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT $this + WHERE { + FILTER NOT EXISTS{ + $this schema:mentions ?action . + ?action schema:additionalType shp:ValidationCheck . + } + } + """ ; + sh:severity sh:Warning ; + sh:message "RootDataEntity SHOULD mention a ValidationCheck object." ; + ] . + + +five-safes-crate:ValidationCheckObjectShouldPointToRootDataEntity + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:sparql [ + a sh:SPARQLConstraint ; + sh:name "object" ; + sh:select """ + PREFIX schema: + PREFIX rocrate: + SELECT $this + WHERE { + FILTER NOT EXISTS { + $this schema:object rocrate:RootDataEntity . + } + } + """ ; + sh:severity sh:Warning ; + sh:message "`ValidationCheck` --> `object` SHOULD point to the root of the RO-Crate" ; + ] . + + +five-safes-crate:ValidationCheckInstrumentShouldPointToEntityWithSpecificId + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "instrument" ; + sh:description "" ; + sh:path schema:instrument ; + sh:minCount 1 ; + sh:nodeKind sh:IRI ; + sh:hasValue ; + sh:severity sh:Warning ; + sh:message "`ValidationCheck` --> `instrument` SHOULD point to an entity with @id https://w3id.org/5s-crate/0.4" ; + ] . + + +five-safes-crate:ValidationCheckShouldHaveActionStatus + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ ; + ] ; + + sh:property [ + a sh:PropertyShape ; + sh:name "ActionStatus" ; + sh:path schema:actionStatus ; + sh:minCount 1 ; + sh:severity sh:Warning ; + sh:message "ValidationCheck SHOULD have actionStatus property." ; + ] . + + +five-safes-crate:DownloadActionShouldHaveEndTimeIfBegun + a sh:NodeShape ; + sh:name "ValidationCheck" ; + sh:description "" ; + sh:target [ + a sh:SPARQLTarget ; + sh:select """ + PREFIX schema: + PREFIX shp: + SELECT ?this + WHERE { + ?this schema:additionalType shp:ValidationCheck ; + 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:description "ValidationCheck SHOULD have the `endTime` property if `actionStatus` is either CompletedActionStatus or FailedActionStatus." ; + sh:message "ValidationCheck SHOULD have the `endTime` property if `actionStatus` is either CompletedActionStatus or FailedActionStatus." ; + ] . diff --git a/tests/integration/profiles/five-safes-crate/test_5src_13_validation_phase.py b/tests/integration/profiles/five-safes-crate/test_5src_13_validation_phase.py new file mode 100644 index 00000000..60ff3afb --- /dev/null +++ b/tests/integration/profiles/five-safes-crate/test_5src_13_validation_phase.py @@ -0,0 +1,353 @@ +# 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_validation_check_not_of_type_assess_action(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?this rdf:type schema:AssessAction . + } + INSERT { + ?this rdf:type . + } + WHERE { + ?this rdf:type schema:AssessAction ; + schema:additionalType shp:ValidationCheck . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=["ValidationCheck MUST be a `schema:AssessAction`."], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_validation_check_name_not_a_string(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?this schema:name ?name . + } + INSERT { + ?this schema:name 123 . + } + WHERE { + ?this schema:additionalType shp:ValidationCheck . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + "ValidationCheck MUST have a human readable name string." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_validation_check_has_action_status_with_not_allowed_value(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:actionStatus ?o . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + schema:actionStatus ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + ( + "actionStatus MUST be either PotentialActionStatus, ActiveActionStatus, " + "CompletedActionStatus, or FailedActionStatus." + ) + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_validation_check_start_time_not_iso_standard(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?c schema:startTime ?t . + } + INSERT { + ?c schema:startTime "1st of Jan 2021" . + } + WHERE { + ?c schema:additionalType shp:ValidationCheck ; + schema:startTime ?t . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + ( + "ValidationCheck --> `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_validation_check_end_time_not_iso_standard(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?c schema:endTime ?t . + } + INSERT { + ?c schema:endTime "1st of Jan 2021" . + } + WHERE { + ?c schema:additionalType shp:ValidationCheck ; + schema:endTime ?t . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.REQUIRED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + ( + "ValidationCheck --> `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, + ) + + +# ----- SHOULD fails tests + + +def test_5src_root_data_entity_does_not_mention_validation_check_entity(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + <./> schema:mentions ?o . + } + WHERE { + ?o schema:additionalType shp:ValidationCheck ; + } + """ + ) + + 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 ValidationCheck object." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_validation_check_object_does_not_point_to_root_data_entity(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:object <./> . + } + INSERT { + ?s schema:object "not the RootDataEntity" . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + "`ValidationCheck` --> `object` SHOULD point to the root of the RO-Crate" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_validation_check_instrument_does_not_point_to_5scrate_0p4(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:instrument . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + schema:instrument . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + "`ValidationCheck` --> `instrument` SHOULD point to an entity with @id https://w3id.org/5s-crate/0.4" + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_Validation_check_does_not_have_action_status_property(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:actionStatus ?o . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + schema:actionStatus ?o . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + "ValidationCheck SHOULD have actionStatus property." + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +def test_5src_download_action_does_not_have_end_time(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:endTime ?time . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + schema:endTime ?time . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.RECOMMENDED, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + ( + "ValidationCheck SHOULD have the `endTime` property if `actionStatus` " + "is either CompletedActionStatus or FailedActionStatus." + ) + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) + + +# ----- MAY fails tests + + +def test_5src_download_action_does_not_have_start_time(): + sparql = ( + SPARQL_PREFIXES + + """ + DELETE { + ?s schema:startTime ?time . + } + WHERE { + ?s schema:additionalType shp:ValidationCheck ; + schema:startTime ?time . + } + """ + ) + + do_entity_test( + rocrate_path=ValidROC().five_safes_crate_result, + requirement_severity=Severity.OPTIONAL, + expected_validation_result=False, + expected_triggered_requirements=["ValidationCheck"], + expected_triggered_issues=[ + ( + "ValidationCheck MAY have the `startTime` property if `actionStatus` " + "is either ActiveActionStatus, CompletedActionStatus or FailedActionStatus." + ) + ], + profile_identifier="five-safes-crate", + rocrate_entity_mod_sparql=sparql, + ) diff --git a/tests/shared.py b/tests/shared.py index c6fcff63..a07d93d1 100644 --- a/tests/shared.py +++ b/tests/shared.py @@ -36,9 +36,10 @@ SPARQL_PREFIXES = """ PREFIX schema: -PREFIX dct: PREFIX shp: PREFIX rdf: +PREFIX rocrate: +PREFIX dct: """