Skip to content

Commit e86d5fe

Browse files
authored
Merge pull request #421 from softwarepub/refactor/384-mark-expected-failures
Extend #403: Mark xfailing tests, raise errors
2 parents 460e3d4 + c72353e commit e86d5fe

3 files changed

Lines changed: 160 additions & 49 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ flake8 = "poetry run flake8 ./test/ ./src/ --count --select=E9,F63,F7,F82 --stat
111111

112112

113113
[tool.pytest.ini_options]
114+
minversion = "6.0"
114115
norecursedirs = "docs/*"
116+
xfail_strict = true
115117
testpaths = [
116118
"test"
117119
]

src/hermes/model/types/ld_context.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# SPDX-FileContributor: Michael Meinel
66
# SPDX-FileContributor: Stephan Druskat <stephan.druskat@dlr.de>
77

8+
from hermes.model.error import HermesContextError
89

910
CODEMETA_PREFIX = "https://doi.org/10.5063/schema/codemeta-2.0"
1011
CODEMETA_CONTEXT = [CODEMETA_PREFIX]
@@ -15,16 +16,26 @@
1516
PROV_PREFIX = "http://www.w3.org/ns/prov#"
1617
PROV_CONTEXT = [{"prov": PROV_PREFIX}]
1718

18-
HERMES_RT_PREFIX = 'https://schema.software-metadata.pub/hermes-runtime/1.0/'
19-
HERMES_RT_CONTEXT = [{'hermes-rt': HERMES_RT_PREFIX}]
20-
HERMES_CONTENT_CONTEXT = [{'hermes': 'https://schema.software-metadata.pub/hermes-content/1.0/'}]
19+
HERMES_RT_PREFIX = "https://schema.software-metadata.pub/hermes-runtime/1.0/"
20+
HERMES_RT_CONTEXT = [{"hermes-rt": HERMES_RT_PREFIX}]
21+
HERMES_CONTENT_CONTEXT = [
22+
{"hermes": "https://schema.software-metadata.pub/hermes-content/1.0/"}
23+
]
2124

2225
HERMES_CONTEXT = [{**HERMES_RT_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]}]
2326

24-
HERMES_BASE_CONTEXT = [*CODEMETA_CONTEXT, {**SCHEMA_ORG_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]}]
25-
HERMES_PROV_CONTEXT = [{**SCHEMA_ORG_CONTEXT[0], **HERMES_RT_CONTEXT[0], **PROV_CONTEXT[0]}]
27+
HERMES_BASE_CONTEXT = [
28+
*CODEMETA_CONTEXT,
29+
{**SCHEMA_ORG_CONTEXT[0], **HERMES_CONTENT_CONTEXT[0]},
30+
]
31+
HERMES_PROV_CONTEXT = [
32+
{**SCHEMA_ORG_CONTEXT[0], **HERMES_RT_CONTEXT[0], **PROV_CONTEXT[0]}
33+
]
2634

27-
ALL_CONTEXTS = [*CODEMETA_CONTEXT, {**SCHEMA_ORG_CONTEXT[0], **PROV_CONTEXT[0], **HERMES_CONTEXT[0]}]
35+
ALL_CONTEXTS = [
36+
*CODEMETA_CONTEXT,
37+
{**SCHEMA_ORG_CONTEXT[0], **PROV_CONTEXT[0], **HERMES_CONTEXT[0]},
38+
]
2839

2940

3041
class ContextPrefix:
@@ -37,6 +48,7 @@ class ContextPrefix:
3748
arbitrary strings used to prefix terms from a specific vocabulary to their respective vocabulary IRI strings.;
3849
- as a dict mapping prefixes to vocabulary IRIs, where the default vocabulary has a prefix of None.
3950
"""
51+
4052
def __init__(self, vocabularies: list[str | dict]):
4153
"""
4254
@param vocabularies: A list of linked data vocabularies. Items can be vocabulary base IRI strings and/or
@@ -54,11 +66,13 @@ def __init__(self, vocabularies: list[str | dict]):
5466
if isinstance(vocab, str):
5567
vocab = {None: vocab}
5668

57-
self.context.update({
58-
prefix: base_iri
59-
for prefix, base_iri in vocab.items()
60-
if isinstance(base_iri, str)
61-
})
69+
self.context.update(
70+
{
71+
prefix: base_iri
72+
for prefix, base_iri in vocab.items()
73+
if isinstance(base_iri, str)
74+
}
75+
)
6276

6377
def __getitem__(self, compressed_term: str | tuple) -> str:
6478
"""
@@ -84,17 +98,21 @@ def __getitem__(self, compressed_term: str | tuple) -> str:
8498
"""
8599
if not isinstance(compressed_term, str):
86100
prefix, term = compressed_term
87-
elif ':' in compressed_term:
88-
prefix, term = compressed_term.split(':', 1)
89-
if term.startswith('://'):
101+
elif ":" in compressed_term:
102+
prefix, term = compressed_term.split(":", 1)
103+
if term.startswith("://"):
90104
prefix, term = True, compressed_term
91-
else:
105+
elif compressed_term != "":
92106
prefix, term = None, compressed_term
107+
else:
108+
raise HermesContextError(compressed_term)
93109

94-
if prefix in self.context:
95-
iri = self.context[prefix] + term
110+
try:
111+
base_iri = self.context[prefix]
112+
except KeyError as ke:
113+
raise HermesContextError(prefix) from ke
96114

97-
return iri
115+
return base_iri + term
98116

99117

100118
iri_map = ContextPrefix(ALL_CONTEXTS)

test/hermes_test/model/types/test_ld_context.py

Lines changed: 122 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
ALL_CONTEXTS,
1111
)
1212

13+
from hermes.model.error import HermesContextError
14+
1315

1416
@pytest.fixture
1517
def ctx():
@@ -18,18 +20,29 @@ def ctx():
1820

1921
def test_ctx():
2022
ctx = ContextPrefix(["u1", {"2": "u2"}])
21-
assert ctx.prefix[None] == "u1"
22-
assert ctx.prefix["2"] == "u2"
23+
assert ctx.context[None] == "u1"
24+
assert ctx.context["2"] == "u2"
2325

2426

27+
@pytest.mark.xfail(
28+
raises=AssertionError,
29+
reason="Currently, the wrong CodeMeta IRI is used in the implementation: "
30+
"https://github.com/softwarepub/hermes/issues/419",
31+
)
2532
def test_codemeta_prefix(ctx):
2633
"""Default vocabulary in context has the correct base IRI."""
27-
assert ctx.prefix[None] == "https://codemeta.github.io/terms/"
34+
assert ctx.context[None] == "https://codemeta.github.io/terms/"
2835

2936

30-
def test_get_codemeta_item(ctx):
37+
@pytest.mark.xfail(
38+
raises=AssertionError,
39+
reason="Currently, the wrong CodeMeta IRI is used in the implementation, so expanding terms doesn't work correctly,"
40+
" see https://github.com/softwarepub/hermes/issues/419",
41+
)
42+
@pytest.mark.parametrize("compacted", ["maintainer", (None, "maintainer")])
43+
def test_get_item_from_default_vocabulary_pass(ctx, compacted):
3144
"""Context returns fully expanded terms for default vocabulary in the context."""
32-
item = ctx["maintainer"]
45+
item = ctx[compacted]
3346
assert item == "https://codemeta.github.io/terms/maintainer"
3447

3548

@@ -41,37 +54,104 @@ def test_get_codemeta_item(ctx):
4154
"hermes:semanticVersion",
4255
"https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion", # TODO: Change on #393 fix
4356
),
57+
(("schema", "Organization"), "http://schema.org/Organization"),
58+
(
59+
("hermes", "semanticVersion"),
60+
"https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion",
61+
), # TODO: Change on #393 fix
4462
],
4563
)
46-
def test_get_prefixed_items(ctx, compacted, expanded):
47-
"""Context returns fully expanded terms for prefixed vocabularies in the context."""
64+
def test_get_item_from_prefixed_vocabulary_pass(ctx, compacted, expanded):
65+
"""
66+
Context returns fully expanded terms for prefixed vocabularies in the context,
67+
for all accepted parameter formats.
68+
"""
4869
item = ctx[compacted]
4970
assert item == expanded
5071

5172

52-
def test_get_protocol_items_pass(ctx):
53-
item = ctx["https://schema.org/Organisation"]
54-
assert item == "https://schema.org/Organisation"
73+
@pytest.mark.parametrize(
74+
"prefix,not_exist",
75+
[
76+
("foobar", item)
77+
for item in [
78+
"foobar:baz",
79+
("foobar", "baz"),
80+
]
81+
],
82+
)
83+
def test_get_item_from_prefixed_vocabulary_raises_on_prefix_not_exist(
84+
ctx, prefix, not_exist
85+
):
86+
"""
87+
Tests that an exception is raised when trying to get compacted items for which there is no
88+
prefixed vocabulary in the context.
89+
"""
90+
with pytest.raises(HermesContextError) as hce:
91+
_ = ctx[not_exist]
92+
assert str(hce.value) == prefix
5593

5694

57-
def test_get_protocol_items_fail(ctx):
58-
with pytest.raises(Exception) as e:
59-
ctx["https://foo.bar/baz"]
60-
assert "cannot access local variable" not in str(e.value) # FIXME: Replace with custom error
95+
@pytest.mark.parametrize(
96+
"term,not_exist",
97+
[
98+
("baz", item)
99+
for item in [
100+
"baz",
101+
"hermes:baz",
102+
"schema:baz",
103+
(None, "baz"),
104+
("hermes", "baz"),
105+
("schema", "baz"),
106+
]
107+
],
108+
)
109+
@pytest.mark.xfail(
110+
raises=NotImplementedError,
111+
reason="Not yet implemented/decided: Check if terms exist in given vocabulary.",
112+
)
113+
def test_get_item_from_prefixed_vocabulary_raises_on_term_not_exist(
114+
ctx, term, not_exist
115+
):
116+
"""
117+
Tests that an exception is raised when trying to get compacted items for which the vocabulary exists,
118+
but doesn't contain the requested term.
119+
"""
120+
with pytest.raises(HermesContextError) as hce:
121+
_ = ctx[not_exist]
122+
with pytest.raises(Exception):
123+
assert str(hce.value) == term
124+
raise NotImplementedError
61125

62126

63127
@pytest.mark.parametrize(
64-
"compacted,expanded",
128+
"expanded",
65129
[
66-
([None, "maintainer"], "https://codemeta.github.io/terms/maintainer"),
67-
(["schema", "Organization"], "http://schema.org/Organization"),
68-
((None, "maintainer"), "https://codemeta.github.io/terms/maintainer"),
69-
(("schema", "Organization"), "http://schema.org/Organization"),
130+
"https://codemeta.github.io/terms/maintainer",
131+
"https://schema.org/Organisation",
132+
"https://schema.software-metadata.pub/hermes-content/1.0/semanticVersion",
70133
],
71134
)
72-
def test_get_valid_non_str_items(ctx, compacted, expanded):
73-
"""Context returns fully expanded terms for valid non-string inputs."""
74-
assert ctx[compacted] == expanded
135+
@pytest.mark.xfail(
136+
raises=NotImplementedError,
137+
reason="Passing back expanded terms on their input if they are valid in the context "
138+
"is not yet implemented (or decided).",
139+
)
140+
def test_get_item_from_expanded_pass(ctx, expanded):
141+
"""
142+
Tests that getting items via their fully expanded terms works as expected.
143+
"""
144+
with pytest.raises(Exception):
145+
assert ctx[expanded] == expanded
146+
raise NotImplementedError
147+
148+
149+
def test_get_item_from_expanded_fail(ctx):
150+
"""
151+
Tests that context raises on unsupported expanded term input.
152+
"""
153+
with pytest.raises(HermesContextError):
154+
ctx["https://foo.bar/baz"]
75155

76156

77157
@pytest.mark.parametrize(
@@ -88,20 +168,31 @@ def test_get_non_str_item_fail(ctx, non_str, error_type):
88168
"item",
89169
[
90170
"",
91-
"fooBar",
171+
pytest.param(
172+
"fooBar",
173+
marks=pytest.mark.xfail(
174+
reason="Not yet implemented/decided: Check if terms exist in given vocabulary."
175+
),
176+
),
92177
[0, "foo"],
93178
(0, "foo"),
94179
{"foo": "bar", "baz": "foo"},
95-
"schema:fooBar",
96-
"hermes:fooBar",
180+
pytest.param(
181+
"schema:fooBar",
182+
marks=pytest.mark.xfail(
183+
reason="Not yet implemented/decided: Check if terms exist in given vocabulary."
184+
),
185+
),
186+
pytest.param(
187+
"hermes:fooBar",
188+
marks=pytest.mark.xfail(
189+
reason="Not yet implemented/decided: Check if terms exist in given vocabulary."
190+
),
191+
),
97192
"codemeta:maintainer", # Prefixed CodeMeta doesn't exist in context
98-
# Even a dict with valid terms should fail, as it is unclear what to expect
99-
{None: "maintainer", "schema": "Organization"},
100193
],
101194
)
102195
def test_get_item_validate_fail(ctx, item):
103-
"""Context raises on terms that don't exist in the context."""
104-
with pytest.raises(
105-
Exception
106-
): # FIXME: Replace with custom error, e.g., hermes.model.errors.InvalidTermException
196+
"""Context raises on theoretically valid compressed terms that don't exist in the context."""
197+
with pytest.raises(HermesContextError):
107198
ctx[item]

0 commit comments

Comments
 (0)