resolve.spec.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. 'use strict';
  2. var Ajv = require('./ajv')
  3. , should = require('./chai').should()
  4. , getAjvInstances = require('./ajv_instances');
  5. describe('resolve', function () {
  6. var instances;
  7. beforeEach(function() {
  8. instances = getAjvInstances({
  9. allErrors: true,
  10. verbose: true,
  11. inlineRefs: false
  12. });
  13. });
  14. describe('resolve.ids method', function() {
  15. it('should resolve ids in schema', function() {
  16. // Example from http://json-schema.org/latest/json-schema-core.html#anchor29
  17. var schema = {
  18. "$id": "http://x.y.z/rootschema.json#",
  19. "schema1": {
  20. "$id": "#foo",
  21. "description": "schema1",
  22. "type": "integer"
  23. },
  24. "schema2": {
  25. "$id": "otherschema.json",
  26. "description": "schema2",
  27. "nested": {
  28. "$id": "#bar",
  29. "description": "nested",
  30. "type": "string"
  31. },
  32. "alsonested": {
  33. "$id": "t/inner.json#a",
  34. "description": "alsonested",
  35. "type": "boolean"
  36. }
  37. },
  38. "schema3": {
  39. "$id": "some://where.else/completely#",
  40. "description": "schema3",
  41. "type": "null"
  42. },
  43. "properties": {
  44. "foo": { "$ref": "#foo" },
  45. "bar": { "$ref": "otherschema.json#bar" },
  46. "baz": { "$ref": "t/inner.json#a" },
  47. "bax": { "$ref": "some://where.else/completely#" }
  48. },
  49. "required": [ "foo", "bar", "baz", "bax" ]
  50. };
  51. instances.forEach(function (ajv) {
  52. var validate = ajv.compile(schema);
  53. var data = { foo: 1, bar: 'abc', baz: true, bax: null };
  54. validate(data) .should.equal(true);
  55. });
  56. });
  57. it('should throw if the same id resolves to two different schemas', function() {
  58. instances.forEach(function (ajv) {
  59. ajv.compile({
  60. "$id": "http://example.com/1.json",
  61. "type": "integer"
  62. });
  63. should.throw(function() {
  64. ajv.compile({
  65. "additionalProperties": {
  66. "$id": "http://example.com/1.json",
  67. "type": "string"
  68. }
  69. });
  70. });
  71. should.throw(function() {
  72. ajv.compile({
  73. "items": {
  74. "$id": "#int",
  75. "type": "integer"
  76. },
  77. "additionalProperties": {
  78. "$id": "#int",
  79. "type": "string"
  80. }
  81. });
  82. });
  83. });
  84. });
  85. it('should resolve ids defined as urn\'s (issue #423)', function() {
  86. var schema = {
  87. "type": "object",
  88. "properties": {
  89. "ip1": {
  90. "$id": "urn:some:ip:prop",
  91. "type": "string",
  92. "format": "ipv4"
  93. },
  94. "ip2": {
  95. "$ref": "urn:some:ip:prop"
  96. }
  97. },
  98. "required": [
  99. "ip1",
  100. "ip2"
  101. ]
  102. };
  103. var data = {
  104. "ip1": "0.0.0.0",
  105. "ip2": "0.0.0.0"
  106. };
  107. instances.forEach(function (ajv) {
  108. var validate = ajv.compile(schema);
  109. validate(data) .should.equal(true);
  110. });
  111. });
  112. });
  113. describe('protocol-relative URIs', function() {
  114. it('should resolve fragment', function() {
  115. instances.forEach(function(ajv) {
  116. var schema = {
  117. "$id": "//e.com/types",
  118. "definitions": {
  119. "int": { "type": "integer" }
  120. }
  121. };
  122. ajv.addSchema(schema);
  123. var validate = ajv.compile({ $ref: '//e.com/types#/definitions/int' });
  124. validate(1) .should.equal(true);
  125. validate('foo') .should.equal(false);
  126. });
  127. });
  128. });
  129. describe('missing schema error', function() {
  130. this.timeout(4000);
  131. it('should contain missingRef and missingSchema', function() {
  132. testMissingSchemaError({
  133. baseId: 'http://example.com/1.json',
  134. ref: 'http://another.com/int.json',
  135. expectedMissingRef: 'http://another.com/int.json',
  136. expectedMissingSchema: 'http://another.com/int.json'
  137. });
  138. });
  139. it('should resolve missingRef and missingSchema relative to base id', function() {
  140. testMissingSchemaError({
  141. baseId: 'http://example.com/folder/1.json',
  142. ref: 'int.json',
  143. expectedMissingRef: 'http://example.com/folder/int.json',
  144. expectedMissingSchema: 'http://example.com/folder/int.json'
  145. });
  146. });
  147. it('should resolve missingRef and missingSchema relative to base id from root', function() {
  148. testMissingSchemaError({
  149. baseId: 'http://example.com/folder/1.json',
  150. ref: '/int.json',
  151. expectedMissingRef: 'http://example.com/int.json',
  152. expectedMissingSchema: 'http://example.com/int.json'
  153. });
  154. });
  155. it('missingRef should and missingSchema should NOT include JSON path (hash fragment)', function() {
  156. testMissingSchemaError({
  157. baseId: 'http://example.com/1.json',
  158. ref: 'int.json#/definitions/positive',
  159. expectedMissingRef: 'http://example.com/int.json#/definitions/positive',
  160. expectedMissingSchema: 'http://example.com/int.json'
  161. });
  162. });
  163. it('should throw missing schema error if same path exist in the current schema but id is different (issue #220)', function() {
  164. testMissingSchemaError({
  165. baseId: 'http://example.com/parent.json',
  166. ref: 'object.json#/properties/a',
  167. expectedMissingRef: 'http://example.com/object.json#/properties/a',
  168. expectedMissingSchema: 'http://example.com/object.json'
  169. });
  170. });
  171. function testMissingSchemaError(opts) {
  172. instances.forEach(function (ajv) {
  173. try {
  174. ajv.compile({
  175. "$id": opts.baseId,
  176. "properties": { "a": { "$ref": opts.ref } }
  177. });
  178. } catch(e) {
  179. e.missingRef .should.equal(opts.expectedMissingRef);
  180. e.missingSchema .should.equal(opts.expectedMissingSchema);
  181. }
  182. });
  183. }
  184. });
  185. describe('inline referenced schemas without refs in them', function() {
  186. var schemas = [
  187. { $id: 'http://e.com/obj.json#',
  188. properties: { a: { $ref: 'int.json#' } } },
  189. { $id: 'http://e.com/int.json#',
  190. type: 'integer', minimum: 2, maximum: 4 },
  191. { $id: 'http://e.com/obj1.json#',
  192. definitions: { int: { type: 'integer', minimum: 2, maximum: 4 } },
  193. properties: { a: { $ref: '#/definitions/int' } } },
  194. { $id: 'http://e.com/list.json#',
  195. items: { $ref: 'obj.json#' } }
  196. ];
  197. it('by default should inline schema if it doesn\'t contain refs', function() {
  198. var ajv = new Ajv({ schemas: schemas });
  199. testSchemas(ajv, true);
  200. });
  201. it('should NOT inline schema if option inlineRefs == false', function() {
  202. var ajv = new Ajv({ schemas: schemas, inlineRefs: false });
  203. testSchemas(ajv, false);
  204. });
  205. it('should inline schema if option inlineRefs is bigger than number of keys in referenced schema', function() {
  206. var ajv = new Ajv({ schemas: schemas, inlineRefs: 3 });
  207. testSchemas(ajv, true);
  208. });
  209. it('should NOT inline schema if option inlineRefs is less than number of keys in referenced schema', function() {
  210. var ajv = new Ajv({ schemas: schemas, inlineRefs: 2 });
  211. testSchemas(ajv, false);
  212. });
  213. it('should avoid schema substitution when refs are inlined (issue #77)', function() {
  214. var ajv = new Ajv({ verbose: true });
  215. var schemaMessage = {
  216. $schema: "http://json-schema.org/draft-07/schema#",
  217. $id: "http://e.com/message.json#",
  218. type: "object",
  219. required: ["header"],
  220. properties: {
  221. header: {
  222. allOf: [
  223. { $ref: "header.json" },
  224. { properties: { msgType: { "enum": [0] } } }
  225. ]
  226. }
  227. }
  228. };
  229. // header schema
  230. var schemaHeader = {
  231. $schema: "http://json-schema.org/draft-07/schema#",
  232. $id: "http://e.com/header.json#",
  233. type: "object",
  234. properties: {
  235. version: {
  236. type: "integer",
  237. maximum: 5
  238. },
  239. msgType: { type: "integer" }
  240. },
  241. required: ["version", "msgType"]
  242. };
  243. // a good message
  244. var validMessage = {
  245. header: {
  246. version: 4,
  247. msgType: 0
  248. }
  249. };
  250. // a bad message
  251. var invalidMessage = {
  252. header: {
  253. version: 6,
  254. msgType: 0
  255. }
  256. };
  257. // add schemas and get validator function
  258. ajv.addSchema(schemaHeader);
  259. ajv.addSchema(schemaMessage);
  260. var v = ajv.getSchema('http://e.com/message.json#');
  261. v(validMessage) .should.equal(true);
  262. v.schema.$id .should.equal('http://e.com/message.json#');
  263. v(invalidMessage) .should.equal(false);
  264. v.errors .should.have.length(1);
  265. v.schema.$id .should.equal('http://e.com/message.json#');
  266. v(validMessage) .should.equal(true);
  267. v.schema.$id .should.equal('http://e.com/message.json#');
  268. });
  269. function testSchemas(ajv, expectedInlined) {
  270. var v1 = ajv.getSchema('http://e.com/obj.json')
  271. , v2 = ajv.getSchema('http://e.com/obj1.json')
  272. , vl = ajv.getSchema('http://e.com/list.json');
  273. testObjSchema(v1);
  274. testObjSchema(v2);
  275. testListSchema(vl);
  276. testInlined(v1, expectedInlined);
  277. testInlined(v2, expectedInlined);
  278. testInlined(vl, false);
  279. }
  280. function testObjSchema(validate) {
  281. validate({a:3}) .should.equal(true);
  282. validate({a:1}) .should.equal(false);
  283. validate({a:5}) .should.equal(false);
  284. }
  285. function testListSchema(validate) {
  286. validate([{a:3}]) .should.equal(true);
  287. validate([{a:1}]) .should.equal(false);
  288. validate([{a:5}]) .should.equal(false);
  289. }
  290. function testInlined(validate, expectedInlined) {
  291. var inlined = !(/refVal/.test(validate.toString()));
  292. inlined .should.equal(expectedInlined);
  293. }
  294. });
  295. });