Skip to content

Commit 2762f4b

Browse files
committed
test(checks): ✅ test external-reference data entities pass the REQUIRED payload check
1 parent e8c170f commit 2762f4b

1 file changed

Lines changed: 88 additions & 22 deletions

File tree

tests/integration/profiles/ro-crate/test_data_entity_metadata.py

Lines changed: 88 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
import logging
1617

17-
from rocrate_validator import models
18+
import pytest
19+
20+
from rocrate_validator import models, services
1821
from tests.conftest import SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER
1922
from tests.ro_crates import InvalidDataEntity, ValidROC
2023
from tests.shared import do_entity_test
@@ -34,7 +37,7 @@ def test_missing_data_entity_reference():
3437
models.Severity.REQUIRED,
3538
False,
3639
["Data Entity: REQUIRED properties"],
37-
["sort-and-change-case.ga", "foo/xxx"]
40+
["sort-and-change-case.ga", "foo/xxx"],
3841
)
3942

4043

@@ -44,7 +47,7 @@ def test_data_entity_must_be_directly_linked():
4447
paths.direct_hasPart_data_entity_reference,
4548
models.Severity.REQUIRED,
4649
True,
47-
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER]
50+
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER],
4851
)
4952

5053

@@ -54,7 +57,7 @@ def test_data_entity_not_linked():
5457
paths.dataset_not_linked_to_root,
5558
models.Severity.REQUIRED,
5659
False,
57-
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER]
60+
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER],
5861
)
5962

6063

@@ -64,7 +67,7 @@ def test_data_entity_must_be_indirectly_linked():
6467
paths.indirect_hasPart_data_entity_reference,
6568
models.Severity.REQUIRED,
6669
True,
67-
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER]
70+
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER],
6871
)
6972

7073

@@ -75,7 +78,7 @@ def test_directory_data_entity_wo_trailing_slash():
7578
models.Severity.RECOMMENDED,
7679
False,
7780
["Directory Data Entity: RECOMMENDED value restriction"],
78-
["Every Data Entity Directory URI SHOULD end with `/`"]
81+
["Every Data Entity Directory URI SHOULD end with `/`"],
7982
)
8083

8184

@@ -86,7 +89,7 @@ def test_missing_data_entity_encoding_format():
8689
models.Severity.RECOMMENDED,
8790
False,
8891
["File Data Entity: RECOMMENDED properties"],
89-
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"]
92+
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"],
9093
)
9194

9295

@@ -97,7 +100,7 @@ def test_invalid_data_entity_encoding_format_pronom():
97100
models.Severity.RECOMMENDED,
98101
False,
99102
["File Data Entity: RECOMMENDED properties"],
100-
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"]
103+
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"],
101104
)
102105

103106

@@ -108,7 +111,7 @@ def test_invalid_data_entity_encoding_format_ctx_website_type():
108111
models.Severity.RECOMMENDED,
109112
False,
110113
["File Data Entity: RECOMMENDED properties"],
111-
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"]
114+
["Missing or invalid `encodingFormat` linked to the `File Data Entity`"],
112115
)
113116

114117

@@ -119,7 +122,7 @@ def test_invalid_data_entity_encoding_format_ctx_website_name():
119122
models.Severity.RECOMMENDED,
120123
False,
121124
["WebSite RECOMMENDED Properties"],
122-
["A WebSite MUST have a `name` property"]
125+
["A WebSite MUST have a `name` property"],
123126
)
124127

125128

@@ -129,7 +132,7 @@ def test_valid_data_entity_encoding_format_pronom():
129132
paths.valid_encoding_format_pronom,
130133
models.Severity.RECOMMENDED,
131134
True,
132-
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER]
135+
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER],
133136
)
134137

135138

@@ -139,7 +142,7 @@ def test_valid_data_entity_encoding_format_ctx_website():
139142
paths.valid_encoding_format_ctx_entity,
140143
models.Severity.RECOMMENDED,
141144
True,
142-
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER]
145+
skip_checks=[SKIP_LOCAL_DATA_ENTITY_EXISTENCE_CHECK_IDENTIFIER],
143146
)
144147

145148

@@ -150,7 +153,7 @@ def test_missing_file_data_entity_with_quoted_name():
150153
models.Severity.REQUIRED,
151154
False,
152155
["Data Entity: REQUIRED resource availability"],
153-
["The RO-Crate does not include the Data Entity 'pics/2017-06-11%2012.56.14.jpg' as part of its payload"]
156+
["The RO-Crate does not include the Data Entity 'pics/2017-06-11%2012.56.14.jpg' as part of its payload"],
154157
)
155158

156159

@@ -161,7 +164,7 @@ def test_missing_file_data_entity_with_unquoted_name():
161164
models.Severity.REQUIRED,
162165
False,
163166
["Data Entity: REQUIRED resource availability"],
164-
["The RO-Crate does not include the Data Entity 'pics/2017-06-11 12.56.14.jpg' as part of its payload"]
167+
["The RO-Crate does not include the Data Entity 'pics/2017-06-11 12.56.14.jpg' as part of its payload"],
165168
)
166169

167170

@@ -172,7 +175,7 @@ def test_missing_dataset_entity_with_quoted_name():
172175
models.Severity.REQUIRED,
173176
False,
174177
["Data Entity: REQUIRED resource availability"],
175-
["The RO-Crate does not include the Data Entity 'data%20set/' as part of its payload"]
178+
["The RO-Crate does not include the Data Entity 'data%20set/' as part of its payload"],
176179
)
177180

178181

@@ -183,7 +186,7 @@ def test_missing_dataset_entity_with_unquoted_name():
183186
models.Severity.REQUIRED,
184187
False,
185188
["Data Entity: REQUIRED resource availability"],
186-
["The RO-Crate does not include the Data Entity 'data set/' as part of its payload"]
189+
["The RO-Crate does not include the Data Entity 'data set/' as part of its payload"],
187190
)
188191

189192

@@ -194,15 +197,78 @@ def test_missing_absolute_path_data_entity():
194197
models.Severity.RECOMMENDED,
195198
False,
196199
["Data Entity: RECOMMENDED resource availability"],
197-
["Data Entity file:///tmp/test.txt is not available"]
200+
["Data Entity file:///tmp/test.txt is not available"],
198201
)
199202

200203

201204
def test_valid_rocrate_with_data_entities():
202205
""""""
203-
do_entity_test(
204-
ValidROC().rocrate_with_data_entities,
205-
models.Severity.REQUIRED,
206-
True,
207-
profile_identifier="ro-crate"
206+
do_entity_test(ValidROC().rocrate_with_data_entities, models.Severity.REQUIRED, True, profile_identifier="ro-crate")
207+
208+
209+
@pytest.mark.parametrize(
210+
"remote_entity_id",
211+
[
212+
"scp://transfer.example.org//data/A.0.0",
213+
"sftp://user@host/path/to/file",
214+
"s3://bucket/key",
215+
],
216+
)
217+
def test_remote_data_entity_does_not_fail_required_check(tmp_path, remote_entity_id):
218+
"""Regression test for issue #176.
219+
220+
A Data Entity whose `@id` is an absolute URI with a non-file scheme (e.g.
221+
``scp://``, ``sftp://``, ``s3://``) MUST NOT trigger the
222+
"Data Entity: REQUIRED resource availability" violation: per the RO-Crate
223+
spec, any absolute-URI Data Entity is web-based and is not required to be
224+
part of the local payload.
225+
"""
226+
crate_dir = tmp_path / "crate-with-remote-entity"
227+
crate_dir.mkdir()
228+
metadata = {
229+
"@context": "https://w3id.org/ro/crate/1.1/context",
230+
"@graph": [
231+
{
232+
"@id": "ro-crate-metadata.json",
233+
"@type": "CreativeWork",
234+
"conformsTo": {"@id": "https://w3id.org/ro/crate/1.1"},
235+
"about": {"@id": "./"},
236+
},
237+
{
238+
"@id": "./",
239+
"@type": "Dataset",
240+
"name": "Crate with remote entity",
241+
"description": "Regression fixture for issue #176",
242+
"datePublished": "2026-05-15T07:30:50+00:00",
243+
"license": {"@id": "https://spdx.org/licenses/CC0-1.0"},
244+
"hasPart": [{"@id": remote_entity_id}],
245+
},
246+
{
247+
"@id": remote_entity_id,
248+
"@type": "File",
249+
"name": "Remote file",
250+
"contentSize": 16,
251+
"dateModified": "2026-05-15T07:30:50+00:00",
252+
"sdDatePublished": "2026-05-15T07:31:03+00:00",
253+
},
254+
{"@id": "https://spdx.org/licenses/CC0-1.0", "@type": "CreativeWork", "name": "CC0"},
255+
],
256+
}
257+
(crate_dir / "ro-crate-metadata.json").write_text(json.dumps(metadata))
258+
259+
result = services.validate(
260+
models.ValidationSettings(
261+
rocrate_uri=crate_dir,
262+
requirement_severity=models.Severity.REQUIRED,
263+
profile_identifier="ro-crate",
264+
)
265+
)
266+
assert result.passed(), (
267+
f"RO-Crate with remote entity '{remote_entity_id}' should pass REQUIRED "
268+
f"validation; got issues: {[i.message for i in result.get_issues()]}"
208269
)
270+
# And the specific must/4 violation must NOT be among the issues.
271+
for issue in result.get_issues():
272+
assert "as part of its payload" not in (issue.message or ""), (
273+
f"Unexpected payload violation raised for remote entity: {issue.message}"
274+
)

0 commit comments

Comments
 (0)