Updates
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
import itertools
|
||||
|
||||
import pytest
|
||||
|
||||
from referencing import Resource, exceptions
|
||||
|
||||
|
||||
def pairs(choices):
|
||||
return itertools.combinations(choices, 2)
|
||||
|
||||
|
||||
TRUE = Resource.opaque(True)
|
||||
|
||||
|
||||
thunks = (
|
||||
lambda: exceptions.CannotDetermineSpecification(TRUE),
|
||||
lambda: exceptions.NoSuchResource("urn:example:foo"),
|
||||
lambda: exceptions.NoInternalID(TRUE),
|
||||
lambda: exceptions.InvalidAnchor(resource=TRUE, anchor="foo", ref="a#b"),
|
||||
lambda: exceptions.NoSuchAnchor(resource=TRUE, anchor="foo", ref="a#b"),
|
||||
lambda: exceptions.PointerToNowhere(resource=TRUE, ref="urn:example:foo"),
|
||||
lambda: exceptions.Unresolvable("urn:example:foo"),
|
||||
lambda: exceptions.Unretrievable("urn:example:foo"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("one, two", pairs(each() for each in thunks))
|
||||
def test_eq_incompatible_types(one, two):
|
||||
assert one != two
|
||||
|
||||
|
||||
@pytest.mark.parametrize("thunk", thunks)
|
||||
def test_hash(thunk):
|
||||
assert thunk() in {thunk()}
|
||||
@@ -0,0 +1,382 @@
|
||||
import pytest
|
||||
|
||||
from referencing import Registry, Resource, Specification
|
||||
import referencing.jsonschema
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"uri, expected",
|
||||
[
|
||||
(
|
||||
"https://json-schema.org/draft/2020-12/schema",
|
||||
referencing.jsonschema.DRAFT202012,
|
||||
),
|
||||
(
|
||||
"https://json-schema.org/draft/2019-09/schema",
|
||||
referencing.jsonschema.DRAFT201909,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-07/schema#",
|
||||
referencing.jsonschema.DRAFT7,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-06/schema#",
|
||||
referencing.jsonschema.DRAFT6,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-04/schema#",
|
||||
referencing.jsonschema.DRAFT4,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-03/schema#",
|
||||
referencing.jsonschema.DRAFT3,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_schemas_with_explicit_schema_keywords_are_detected(uri, expected):
|
||||
"""
|
||||
The $schema keyword in JSON Schema is a dialect identifier.
|
||||
"""
|
||||
contents = {"$schema": uri}
|
||||
resource = Resource.from_contents(contents)
|
||||
assert resource == Resource(contents=contents, specification=expected)
|
||||
|
||||
|
||||
def test_unknown_dialect():
|
||||
dialect_id = "http://example.com/unknown-json-schema-dialect-id"
|
||||
with pytest.raises(referencing.jsonschema.UnknownDialect) as excinfo:
|
||||
Resource.from_contents({"$schema": dialect_id})
|
||||
assert excinfo.value.uri == dialect_id
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"id, specification",
|
||||
[
|
||||
("$id", referencing.jsonschema.DRAFT202012),
|
||||
("$id", referencing.jsonschema.DRAFT201909),
|
||||
("$id", referencing.jsonschema.DRAFT7),
|
||||
("$id", referencing.jsonschema.DRAFT6),
|
||||
("id", referencing.jsonschema.DRAFT4),
|
||||
("id", referencing.jsonschema.DRAFT3),
|
||||
],
|
||||
)
|
||||
def test_id_of_mapping(id, specification):
|
||||
uri = "http://example.com/some-schema"
|
||||
assert specification.id_of({id: uri}) == uri
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"specification",
|
||||
[
|
||||
referencing.jsonschema.DRAFT202012,
|
||||
referencing.jsonschema.DRAFT201909,
|
||||
referencing.jsonschema.DRAFT7,
|
||||
referencing.jsonschema.DRAFT6,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("value", [True, False])
|
||||
def test_id_of_bool(specification, value):
|
||||
assert specification.id_of(value) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"specification",
|
||||
[
|
||||
referencing.jsonschema.DRAFT202012,
|
||||
referencing.jsonschema.DRAFT201909,
|
||||
referencing.jsonschema.DRAFT7,
|
||||
referencing.jsonschema.DRAFT6,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("value", [True, False])
|
||||
def test_anchors_in_bool(specification, value):
|
||||
assert list(specification.anchors_in(value)) == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"specification",
|
||||
[
|
||||
referencing.jsonschema.DRAFT202012,
|
||||
referencing.jsonschema.DRAFT201909,
|
||||
referencing.jsonschema.DRAFT7,
|
||||
referencing.jsonschema.DRAFT6,
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("value", [True, False])
|
||||
def test_subresources_of_bool(specification, value):
|
||||
assert list(specification.subresources_of(value)) == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"uri, expected",
|
||||
[
|
||||
(
|
||||
"https://json-schema.org/draft/2020-12/schema",
|
||||
referencing.jsonschema.DRAFT202012,
|
||||
),
|
||||
(
|
||||
"https://json-schema.org/draft/2019-09/schema",
|
||||
referencing.jsonschema.DRAFT201909,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-07/schema#",
|
||||
referencing.jsonschema.DRAFT7,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-06/schema#",
|
||||
referencing.jsonschema.DRAFT6,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-04/schema#",
|
||||
referencing.jsonschema.DRAFT4,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-03/schema#",
|
||||
referencing.jsonschema.DRAFT3,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_specification_with(uri, expected):
|
||||
assert referencing.jsonschema.specification_with(uri) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"uri, expected",
|
||||
[
|
||||
(
|
||||
"http://json-schema.org/draft-07/schema",
|
||||
referencing.jsonschema.DRAFT7,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-06/schema",
|
||||
referencing.jsonschema.DRAFT6,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-04/schema",
|
||||
referencing.jsonschema.DRAFT4,
|
||||
),
|
||||
(
|
||||
"http://json-schema.org/draft-03/schema",
|
||||
referencing.jsonschema.DRAFT3,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_specification_with_no_empty_fragment(uri, expected):
|
||||
assert referencing.jsonschema.specification_with(uri) == expected
|
||||
|
||||
|
||||
def test_specification_with_unknown_dialect():
|
||||
dialect_id = "http://example.com/unknown-json-schema-dialect-id"
|
||||
with pytest.raises(referencing.jsonschema.UnknownDialect) as excinfo:
|
||||
referencing.jsonschema.specification_with(dialect_id)
|
||||
assert excinfo.value.uri == dialect_id
|
||||
|
||||
|
||||
def test_specification_with_default():
|
||||
dialect_id = "http://example.com/unknown-json-schema-dialect-id"
|
||||
specification = referencing.jsonschema.specification_with(
|
||||
dialect_id,
|
||||
default=Specification.OPAQUE,
|
||||
)
|
||||
assert specification is Specification.OPAQUE
|
||||
|
||||
|
||||
# FIXME: The tests below should move to the referencing suite but I haven't yet
|
||||
# figured out how to represent dynamic (& recursive) ref lookups in it.
|
||||
def test_lookup_trivial_dynamic_ref():
|
||||
one = referencing.jsonschema.DRAFT202012.create_resource(
|
||||
{"$dynamicAnchor": "foo"},
|
||||
)
|
||||
resolver = Registry().with_resource("http://example.com", one).resolver()
|
||||
resolved = resolver.lookup("http://example.com#foo")
|
||||
assert resolved.contents == one.contents
|
||||
|
||||
|
||||
def test_multiple_lookup_trivial_dynamic_ref():
|
||||
TRUE = referencing.jsonschema.DRAFT202012.create_resource(True)
|
||||
root = referencing.jsonschema.DRAFT202012.create_resource(
|
||||
{
|
||||
"$id": "http://example.com",
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
"$defs": {
|
||||
"foo": {
|
||||
"$id": "foo",
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
"$defs": {
|
||||
"bar": True,
|
||||
"baz": {
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
resolver = (
|
||||
Registry()
|
||||
.with_resources(
|
||||
[
|
||||
("http://example.com", root),
|
||||
("http://example.com/foo/", TRUE),
|
||||
("http://example.com/foo/bar", root),
|
||||
],
|
||||
)
|
||||
.resolver()
|
||||
)
|
||||
|
||||
first = resolver.lookup("http://example.com")
|
||||
second = first.resolver.lookup("foo/")
|
||||
resolver = second.resolver.lookup("bar").resolver
|
||||
fourth = resolver.lookup("#fooAnchor")
|
||||
assert fourth.contents == root.contents
|
||||
|
||||
|
||||
def test_multiple_lookup_dynamic_ref_to_nondynamic_ref():
|
||||
one = referencing.jsonschema.DRAFT202012.create_resource(
|
||||
{"$anchor": "fooAnchor"},
|
||||
)
|
||||
two = referencing.jsonschema.DRAFT202012.create_resource(
|
||||
{
|
||||
"$id": "http://example.com",
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
"$defs": {
|
||||
"foo": {
|
||||
"$id": "foo",
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
"$defs": {
|
||||
"bar": True,
|
||||
"baz": {
|
||||
"$dynamicAnchor": "fooAnchor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
resolver = (
|
||||
Registry()
|
||||
.with_resources(
|
||||
[
|
||||
("http://example.com", two),
|
||||
("http://example.com/foo/", one),
|
||||
("http://example.com/foo/bar", two),
|
||||
],
|
||||
)
|
||||
.resolver()
|
||||
)
|
||||
|
||||
first = resolver.lookup("http://example.com")
|
||||
second = first.resolver.lookup("foo/")
|
||||
resolver = second.resolver.lookup("bar").resolver
|
||||
fourth = resolver.lookup("#fooAnchor")
|
||||
assert fourth.contents == two.contents
|
||||
|
||||
|
||||
def test_lookup_trivial_recursive_ref():
|
||||
one = referencing.jsonschema.DRAFT201909.create_resource(
|
||||
{"$recursiveAnchor": True},
|
||||
)
|
||||
resolver = Registry().with_resource("http://example.com", one).resolver()
|
||||
first = resolver.lookup("http://example.com")
|
||||
resolved = referencing.jsonschema.lookup_recursive_ref(
|
||||
resolver=first.resolver,
|
||||
)
|
||||
assert resolved.contents == one.contents
|
||||
|
||||
|
||||
def test_lookup_recursive_ref_to_bool():
|
||||
TRUE = referencing.jsonschema.DRAFT201909.create_resource(True)
|
||||
registry = Registry({"http://example.com": TRUE})
|
||||
resolved = referencing.jsonschema.lookup_recursive_ref(
|
||||
resolver=registry.resolver(base_uri="http://example.com"),
|
||||
)
|
||||
assert resolved.contents == TRUE.contents
|
||||
|
||||
|
||||
def test_multiple_lookup_recursive_ref_to_bool():
|
||||
TRUE = referencing.jsonschema.DRAFT201909.create_resource(True)
|
||||
root = referencing.jsonschema.DRAFT201909.create_resource(
|
||||
{
|
||||
"$id": "http://example.com",
|
||||
"$recursiveAnchor": True,
|
||||
"$defs": {
|
||||
"foo": {
|
||||
"$id": "foo",
|
||||
"$recursiveAnchor": True,
|
||||
"$defs": {
|
||||
"bar": True,
|
||||
"baz": {
|
||||
"$recursiveAnchor": True,
|
||||
"$anchor": "fooAnchor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
resolver = (
|
||||
Registry()
|
||||
.with_resources(
|
||||
[
|
||||
("http://example.com", root),
|
||||
("http://example.com/foo/", TRUE),
|
||||
("http://example.com/foo/bar", root),
|
||||
],
|
||||
)
|
||||
.resolver()
|
||||
)
|
||||
|
||||
first = resolver.lookup("http://example.com")
|
||||
second = first.resolver.lookup("foo/")
|
||||
resolver = second.resolver.lookup("bar").resolver
|
||||
fourth = referencing.jsonschema.lookup_recursive_ref(resolver=resolver)
|
||||
assert fourth.contents == root.contents
|
||||
|
||||
|
||||
def test_multiple_lookup_recursive_ref_with_nonrecursive_ref():
|
||||
one = referencing.jsonschema.DRAFT201909.create_resource(
|
||||
{"$recursiveAnchor": True},
|
||||
)
|
||||
two = referencing.jsonschema.DRAFT201909.create_resource(
|
||||
{
|
||||
"$id": "http://example.com",
|
||||
"$recursiveAnchor": True,
|
||||
"$defs": {
|
||||
"foo": {
|
||||
"$id": "foo",
|
||||
"$recursiveAnchor": True,
|
||||
"$defs": {
|
||||
"bar": True,
|
||||
"baz": {
|
||||
"$recursiveAnchor": True,
|
||||
"$anchor": "fooAnchor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
three = referencing.jsonschema.DRAFT201909.create_resource(
|
||||
{"$recursiveAnchor": False},
|
||||
)
|
||||
resolver = (
|
||||
Registry()
|
||||
.with_resources(
|
||||
[
|
||||
("http://example.com", three),
|
||||
("http://example.com/foo/", two),
|
||||
("http://example.com/foo/bar", one),
|
||||
],
|
||||
)
|
||||
.resolver()
|
||||
)
|
||||
|
||||
first = resolver.lookup("http://example.com")
|
||||
second = first.resolver.lookup("foo/")
|
||||
resolver = second.resolver.lookup("bar").resolver
|
||||
fourth = referencing.jsonschema.lookup_recursive_ref(resolver=resolver)
|
||||
assert fourth.contents == two.contents
|
||||
|
||||
|
||||
def test_empty_registry():
|
||||
assert referencing.jsonschema.EMPTY_REGISTRY == Registry()
|
||||
@@ -0,0 +1,66 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from referencing import Registry
|
||||
from referencing.exceptions import Unresolvable
|
||||
import referencing.jsonschema
|
||||
|
||||
|
||||
class SuiteNotFound(Exception):
|
||||
def __str__(self): # pragma: no cover
|
||||
return (
|
||||
"Cannot find the referencing suite. "
|
||||
"Set the REFERENCING_SUITE environment variable to the path to "
|
||||
"the suite, or run the test suite from alongside a full checkout "
|
||||
"of the git repository."
|
||||
)
|
||||
|
||||
|
||||
if "REFERENCING_SUITE" in os.environ: # pragma: no cover
|
||||
SUITE = Path(os.environ["REFERENCING_SUITE"]) / "tests"
|
||||
else:
|
||||
SUITE = Path(__file__).parent.parent.parent / "suite/tests"
|
||||
if not SUITE.is_dir(): # pragma: no cover
|
||||
raise SuiteNotFound()
|
||||
DIALECT_IDS = json.loads(SUITE.joinpath("specifications.json").read_text())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_path",
|
||||
[
|
||||
pytest.param(each, id=f"{each.parent.name}-{each.stem}")
|
||||
for each in SUITE.glob("*/**/*.json")
|
||||
],
|
||||
)
|
||||
def test_referencing_suite(test_path, subtests):
|
||||
dialect_id = DIALECT_IDS[test_path.relative_to(SUITE).parts[0]]
|
||||
specification = referencing.jsonschema.specification_with(dialect_id)
|
||||
loaded = json.loads(test_path.read_text())
|
||||
registry = loaded["registry"]
|
||||
registry = Registry().with_resources(
|
||||
(uri, specification.create_resource(contents))
|
||||
for uri, contents in loaded["registry"].items()
|
||||
)
|
||||
for test in loaded["tests"]:
|
||||
with subtests.test(test=test):
|
||||
if "normalization" in test_path.stem:
|
||||
pytest.xfail("APIs need to change for proper URL support.")
|
||||
|
||||
resolver = registry.resolver(base_uri=test.get("base_uri", ""))
|
||||
|
||||
if test.get("error"):
|
||||
with pytest.raises(Unresolvable):
|
||||
resolver.lookup(test["ref"])
|
||||
else:
|
||||
resolved = resolver.lookup(test["ref"])
|
||||
assert resolved.contents == test["target"]
|
||||
|
||||
then = test.get("then")
|
||||
while then: # pragma: no cover
|
||||
with subtests.test(test=test, then=then):
|
||||
resolved = resolved.resolver.lookup(then["ref"])
|
||||
assert resolved.contents == then["target"]
|
||||
then = then.get("then")
|
||||
@@ -0,0 +1,106 @@
|
||||
from functools import lru_cache
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from referencing import Registry, Resource, exceptions
|
||||
from referencing.jsonschema import DRAFT202012
|
||||
from referencing.retrieval import to_cached_resource
|
||||
|
||||
|
||||
class TestToCachedResource:
|
||||
def test_it_caches_retrieved_resources(self):
|
||||
contents = {"$schema": "https://json-schema.org/draft/2020-12/schema"}
|
||||
stack = [json.dumps(contents)]
|
||||
|
||||
@to_cached_resource()
|
||||
def retrieve(uri):
|
||||
return stack.pop()
|
||||
|
||||
registry = Registry(retrieve=retrieve)
|
||||
|
||||
expected = Resource.from_contents(contents)
|
||||
|
||||
got = registry.get_or_retrieve("urn:example:schema")
|
||||
assert got.value == expected
|
||||
|
||||
# And a second time we get the same value.
|
||||
again = registry.get_or_retrieve("urn:example:schema")
|
||||
assert again.value is got.value
|
||||
|
||||
def test_custom_loader(self):
|
||||
contents = {"$schema": "https://json-schema.org/draft/2020-12/schema"}
|
||||
stack = [json.dumps(contents)[::-1]]
|
||||
|
||||
@to_cached_resource(loads=lambda s: json.loads(s[::-1]))
|
||||
def retrieve(uri):
|
||||
return stack.pop()
|
||||
|
||||
registry = Registry(retrieve=retrieve)
|
||||
|
||||
expected = Resource.from_contents(contents)
|
||||
|
||||
got = registry.get_or_retrieve("urn:example:schema")
|
||||
assert got.value == expected
|
||||
|
||||
# And a second time we get the same value.
|
||||
again = registry.get_or_retrieve("urn:example:schema")
|
||||
assert again.value is got.value
|
||||
|
||||
def test_custom_from_contents(self):
|
||||
contents = {}
|
||||
stack = [json.dumps(contents)]
|
||||
|
||||
@to_cached_resource(from_contents=DRAFT202012.create_resource)
|
||||
def retrieve(uri):
|
||||
return stack.pop()
|
||||
|
||||
registry = Registry(retrieve=retrieve)
|
||||
|
||||
expected = DRAFT202012.create_resource(contents)
|
||||
|
||||
got = registry.get_or_retrieve("urn:example:schema")
|
||||
assert got.value == expected
|
||||
|
||||
# And a second time we get the same value.
|
||||
again = registry.get_or_retrieve("urn:example:schema")
|
||||
assert again.value is got.value
|
||||
|
||||
def test_custom_cache(self):
|
||||
schema = {"$schema": "https://json-schema.org/draft/2020-12/schema"}
|
||||
mapping = {
|
||||
"urn:example:1": dict(schema, foo=1),
|
||||
"urn:example:2": dict(schema, foo=2),
|
||||
"urn:example:3": dict(schema, foo=3),
|
||||
}
|
||||
|
||||
resources = {
|
||||
uri: Resource.from_contents(contents)
|
||||
for uri, contents in mapping.items()
|
||||
}
|
||||
|
||||
@to_cached_resource(cache=lru_cache(maxsize=2))
|
||||
def retrieve(uri):
|
||||
return json.dumps(mapping.pop(uri))
|
||||
|
||||
registry = Registry(retrieve=retrieve)
|
||||
|
||||
got = registry.get_or_retrieve("urn:example:1")
|
||||
assert got.value == resources["urn:example:1"]
|
||||
assert registry.get_or_retrieve("urn:example:1").value is got.value
|
||||
assert registry.get_or_retrieve("urn:example:1").value is got.value
|
||||
|
||||
got = registry.get_or_retrieve("urn:example:2")
|
||||
assert got.value == resources["urn:example:2"]
|
||||
assert registry.get_or_retrieve("urn:example:2").value is got.value
|
||||
assert registry.get_or_retrieve("urn:example:2").value is got.value
|
||||
|
||||
# This still succeeds, but evicts the first URI
|
||||
got = registry.get_or_retrieve("urn:example:3")
|
||||
assert got.value == resources["urn:example:3"]
|
||||
assert registry.get_or_retrieve("urn:example:3").value is got.value
|
||||
assert registry.get_or_retrieve("urn:example:3").value is got.value
|
||||
|
||||
# And now this fails (as we popped the value out of `mapping`)
|
||||
with pytest.raises(exceptions.Unretrievable):
|
||||
registry.get_or_retrieve("urn:example:1")
|
||||
Reference in New Issue
Block a user