test_jsonschema.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. import pytest
  2. from referencing import Registry, Resource, Specification
  3. import referencing.jsonschema
  4. @pytest.mark.parametrize(
  5. "uri, expected",
  6. [
  7. (
  8. "https://json-schema.org/draft/2020-12/schema",
  9. referencing.jsonschema.DRAFT202012,
  10. ),
  11. (
  12. "https://json-schema.org/draft/2019-09/schema",
  13. referencing.jsonschema.DRAFT201909,
  14. ),
  15. (
  16. "http://json-schema.org/draft-07/schema#",
  17. referencing.jsonschema.DRAFT7,
  18. ),
  19. (
  20. "http://json-schema.org/draft-06/schema#",
  21. referencing.jsonschema.DRAFT6,
  22. ),
  23. (
  24. "http://json-schema.org/draft-04/schema#",
  25. referencing.jsonschema.DRAFT4,
  26. ),
  27. (
  28. "http://json-schema.org/draft-03/schema#",
  29. referencing.jsonschema.DRAFT3,
  30. ),
  31. ],
  32. )
  33. def test_schemas_with_explicit_schema_keywords_are_detected(uri, expected):
  34. """
  35. The $schema keyword in JSON Schema is a dialect identifier.
  36. """
  37. contents = {"$schema": uri}
  38. resource = Resource.from_contents(contents)
  39. assert resource == Resource(contents=contents, specification=expected)
  40. def test_unknown_dialect():
  41. dialect_id = "http://example.com/unknown-json-schema-dialect-id"
  42. with pytest.raises(referencing.jsonschema.UnknownDialect) as excinfo:
  43. Resource.from_contents({"$schema": dialect_id})
  44. assert excinfo.value.uri == dialect_id
  45. @pytest.mark.parametrize(
  46. "id, specification",
  47. [
  48. ("$id", referencing.jsonschema.DRAFT202012),
  49. ("$id", referencing.jsonschema.DRAFT201909),
  50. ("$id", referencing.jsonschema.DRAFT7),
  51. ("$id", referencing.jsonschema.DRAFT6),
  52. ("id", referencing.jsonschema.DRAFT4),
  53. ("id", referencing.jsonschema.DRAFT3),
  54. ],
  55. )
  56. def test_id_of_mapping(id, specification):
  57. uri = "http://example.com/some-schema"
  58. assert specification.id_of({id: uri}) == uri
  59. @pytest.mark.parametrize(
  60. "specification",
  61. [
  62. referencing.jsonschema.DRAFT202012,
  63. referencing.jsonschema.DRAFT201909,
  64. referencing.jsonschema.DRAFT7,
  65. referencing.jsonschema.DRAFT6,
  66. ],
  67. )
  68. @pytest.mark.parametrize("value", [True, False])
  69. def test_id_of_bool(specification, value):
  70. assert specification.id_of(value) is None
  71. @pytest.mark.parametrize(
  72. "specification",
  73. [
  74. referencing.jsonschema.DRAFT202012,
  75. referencing.jsonschema.DRAFT201909,
  76. referencing.jsonschema.DRAFT7,
  77. referencing.jsonschema.DRAFT6,
  78. ],
  79. )
  80. @pytest.mark.parametrize("value", [True, False])
  81. def test_anchors_in_bool(specification, value):
  82. assert list(specification.anchors_in(value)) == []
  83. @pytest.mark.parametrize(
  84. "specification",
  85. [
  86. referencing.jsonschema.DRAFT202012,
  87. referencing.jsonschema.DRAFT201909,
  88. referencing.jsonschema.DRAFT7,
  89. referencing.jsonschema.DRAFT6,
  90. ],
  91. )
  92. @pytest.mark.parametrize("value", [True, False])
  93. def test_subresources_of_bool(specification, value):
  94. assert list(specification.subresources_of(value)) == []
  95. @pytest.mark.parametrize(
  96. "uri, expected",
  97. [
  98. (
  99. "https://json-schema.org/draft/2020-12/schema",
  100. referencing.jsonschema.DRAFT202012,
  101. ),
  102. (
  103. "https://json-schema.org/draft/2019-09/schema",
  104. referencing.jsonschema.DRAFT201909,
  105. ),
  106. (
  107. "http://json-schema.org/draft-07/schema#",
  108. referencing.jsonschema.DRAFT7,
  109. ),
  110. (
  111. "http://json-schema.org/draft-06/schema#",
  112. referencing.jsonschema.DRAFT6,
  113. ),
  114. (
  115. "http://json-schema.org/draft-04/schema#",
  116. referencing.jsonschema.DRAFT4,
  117. ),
  118. (
  119. "http://json-schema.org/draft-03/schema#",
  120. referencing.jsonschema.DRAFT3,
  121. ),
  122. ],
  123. )
  124. def test_specification_with(uri, expected):
  125. assert referencing.jsonschema.specification_with(uri) == expected
  126. @pytest.mark.parametrize(
  127. "uri, expected",
  128. [
  129. (
  130. "http://json-schema.org/draft-07/schema",
  131. referencing.jsonschema.DRAFT7,
  132. ),
  133. (
  134. "http://json-schema.org/draft-06/schema",
  135. referencing.jsonschema.DRAFT6,
  136. ),
  137. (
  138. "http://json-schema.org/draft-04/schema",
  139. referencing.jsonschema.DRAFT4,
  140. ),
  141. (
  142. "http://json-schema.org/draft-03/schema",
  143. referencing.jsonschema.DRAFT3,
  144. ),
  145. ],
  146. )
  147. def test_specification_with_no_empty_fragment(uri, expected):
  148. assert referencing.jsonschema.specification_with(uri) == expected
  149. def test_specification_with_unknown_dialect():
  150. dialect_id = "http://example.com/unknown-json-schema-dialect-id"
  151. with pytest.raises(referencing.jsonschema.UnknownDialect) as excinfo:
  152. referencing.jsonschema.specification_with(dialect_id)
  153. assert excinfo.value.uri == dialect_id
  154. def test_specification_with_default():
  155. dialect_id = "http://example.com/unknown-json-schema-dialect-id"
  156. specification = referencing.jsonschema.specification_with(
  157. dialect_id,
  158. default=Specification.OPAQUE,
  159. )
  160. assert specification is Specification.OPAQUE
  161. # FIXME: The tests below should move to the referencing suite but I haven't yet
  162. # figured out how to represent dynamic (& recursive) ref lookups in it.
  163. def test_lookup_trivial_dynamic_ref():
  164. one = referencing.jsonschema.DRAFT202012.create_resource(
  165. {"$dynamicAnchor": "foo"},
  166. )
  167. resolver = Registry().with_resource("http://example.com", one).resolver()
  168. resolved = resolver.lookup("http://example.com#foo")
  169. assert resolved.contents == one.contents
  170. def test_multiple_lookup_trivial_dynamic_ref():
  171. TRUE = referencing.jsonschema.DRAFT202012.create_resource(True)
  172. root = referencing.jsonschema.DRAFT202012.create_resource(
  173. {
  174. "$id": "http://example.com",
  175. "$dynamicAnchor": "fooAnchor",
  176. "$defs": {
  177. "foo": {
  178. "$id": "foo",
  179. "$dynamicAnchor": "fooAnchor",
  180. "$defs": {
  181. "bar": True,
  182. "baz": {
  183. "$dynamicAnchor": "fooAnchor",
  184. },
  185. },
  186. },
  187. },
  188. },
  189. )
  190. resolver = (
  191. Registry()
  192. .with_resources(
  193. [
  194. ("http://example.com", root),
  195. ("http://example.com/foo/", TRUE),
  196. ("http://example.com/foo/bar", root),
  197. ],
  198. )
  199. .resolver()
  200. )
  201. first = resolver.lookup("http://example.com")
  202. second = first.resolver.lookup("foo/")
  203. resolver = second.resolver.lookup("bar").resolver
  204. fourth = resolver.lookup("#fooAnchor")
  205. assert fourth.contents == root.contents
  206. def test_multiple_lookup_dynamic_ref_to_nondynamic_ref():
  207. one = referencing.jsonschema.DRAFT202012.create_resource(
  208. {"$anchor": "fooAnchor"},
  209. )
  210. two = referencing.jsonschema.DRAFT202012.create_resource(
  211. {
  212. "$id": "http://example.com",
  213. "$dynamicAnchor": "fooAnchor",
  214. "$defs": {
  215. "foo": {
  216. "$id": "foo",
  217. "$dynamicAnchor": "fooAnchor",
  218. "$defs": {
  219. "bar": True,
  220. "baz": {
  221. "$dynamicAnchor": "fooAnchor",
  222. },
  223. },
  224. },
  225. },
  226. },
  227. )
  228. resolver = (
  229. Registry()
  230. .with_resources(
  231. [
  232. ("http://example.com", two),
  233. ("http://example.com/foo/", one),
  234. ("http://example.com/foo/bar", two),
  235. ],
  236. )
  237. .resolver()
  238. )
  239. first = resolver.lookup("http://example.com")
  240. second = first.resolver.lookup("foo/")
  241. resolver = second.resolver.lookup("bar").resolver
  242. fourth = resolver.lookup("#fooAnchor")
  243. assert fourth.contents == two.contents
  244. def test_lookup_trivial_recursive_ref():
  245. one = referencing.jsonschema.DRAFT201909.create_resource(
  246. {"$recursiveAnchor": True},
  247. )
  248. resolver = Registry().with_resource("http://example.com", one).resolver()
  249. first = resolver.lookup("http://example.com")
  250. resolved = referencing.jsonschema.lookup_recursive_ref(
  251. resolver=first.resolver,
  252. )
  253. assert resolved.contents == one.contents
  254. def test_lookup_recursive_ref_to_bool():
  255. TRUE = referencing.jsonschema.DRAFT201909.create_resource(True)
  256. registry = Registry({"http://example.com": TRUE})
  257. resolved = referencing.jsonschema.lookup_recursive_ref(
  258. resolver=registry.resolver(base_uri="http://example.com"),
  259. )
  260. assert resolved.contents == TRUE.contents
  261. def test_multiple_lookup_recursive_ref_to_bool():
  262. TRUE = referencing.jsonschema.DRAFT201909.create_resource(True)
  263. root = referencing.jsonschema.DRAFT201909.create_resource(
  264. {
  265. "$id": "http://example.com",
  266. "$recursiveAnchor": True,
  267. "$defs": {
  268. "foo": {
  269. "$id": "foo",
  270. "$recursiveAnchor": True,
  271. "$defs": {
  272. "bar": True,
  273. "baz": {
  274. "$recursiveAnchor": True,
  275. "$anchor": "fooAnchor",
  276. },
  277. },
  278. },
  279. },
  280. },
  281. )
  282. resolver = (
  283. Registry()
  284. .with_resources(
  285. [
  286. ("http://example.com", root),
  287. ("http://example.com/foo/", TRUE),
  288. ("http://example.com/foo/bar", root),
  289. ],
  290. )
  291. .resolver()
  292. )
  293. first = resolver.lookup("http://example.com")
  294. second = first.resolver.lookup("foo/")
  295. resolver = second.resolver.lookup("bar").resolver
  296. fourth = referencing.jsonschema.lookup_recursive_ref(resolver=resolver)
  297. assert fourth.contents == root.contents
  298. def test_multiple_lookup_recursive_ref_with_nonrecursive_ref():
  299. one = referencing.jsonschema.DRAFT201909.create_resource(
  300. {"$recursiveAnchor": True},
  301. )
  302. two = referencing.jsonschema.DRAFT201909.create_resource(
  303. {
  304. "$id": "http://example.com",
  305. "$recursiveAnchor": True,
  306. "$defs": {
  307. "foo": {
  308. "$id": "foo",
  309. "$recursiveAnchor": True,
  310. "$defs": {
  311. "bar": True,
  312. "baz": {
  313. "$recursiveAnchor": True,
  314. "$anchor": "fooAnchor",
  315. },
  316. },
  317. },
  318. },
  319. },
  320. )
  321. three = referencing.jsonschema.DRAFT201909.create_resource(
  322. {"$recursiveAnchor": False},
  323. )
  324. resolver = (
  325. Registry()
  326. .with_resources(
  327. [
  328. ("http://example.com", three),
  329. ("http://example.com/foo/", two),
  330. ("http://example.com/foo/bar", one),
  331. ],
  332. )
  333. .resolver()
  334. )
  335. first = resolver.lookup("http://example.com")
  336. second = first.resolver.lookup("foo/")
  337. resolver = second.resolver.lookup("bar").resolver
  338. fourth = referencing.jsonschema.lookup_recursive_ref(resolver=resolver)
  339. assert fourth.contents == two.contents
  340. def test_empty_registry():
  341. assert referencing.jsonschema.EMPTY_REGISTRY == Registry()