dato_json_schema 0.20.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ require "test_helper"
2
+
3
+ require "json_schema"
4
+
5
+ describe JsonSchema::DocumentStore do
6
+ before do
7
+ @store = JsonSchema::DocumentStore.new
8
+ end
9
+
10
+ it "adds and looks up a schema" do
11
+ schema = schema_sample("http://example.com/schema")
12
+ @store.add_schema(schema)
13
+ assert_equal schema, @store.lookup_schema(schema.uri)
14
+ end
15
+
16
+ it "can iterate through its schemas" do
17
+ uri = "http://example.com/schema"
18
+ schema = schema_sample(uri)
19
+ @store.add_schema(schema)
20
+ assert_equal [[uri, schema]], @store.to_a
21
+ end
22
+
23
+ it "can lookup a schema added with a document root sign" do
24
+ uri = "http://example.com/schema"
25
+ schema = schema_sample(uri + "#")
26
+ @store.add_schema(schema)
27
+ assert_equal schema, @store.lookup_schema(uri)
28
+ end
29
+
30
+ it "can lookup a schema with a document root sign" do
31
+ uri = "http://example.com/schema"
32
+ schema = schema_sample(uri)
33
+ @store.add_schema(schema)
34
+ assert_equal schema, @store.lookup_schema(uri + "#")
35
+ end
36
+
37
+ def schema_sample(uri)
38
+ schema = JsonSchema::Schema.new
39
+ schema.uri = uri
40
+ schema
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ require "test_helper"
2
+
3
+ require "json_schema"
4
+
5
+ describe JsonSchema::SchemaError do
6
+ it "can print a message with a pointer" do
7
+ schema = JsonSchema::Schema.new
8
+ schema.fragment = "#"
9
+
10
+ e = JsonSchema::SchemaError.new(schema, "problem", nil)
11
+ assert_equal "#: problem", e.to_s
12
+ end
13
+
14
+ it "can print a message without a pointer" do
15
+ e = JsonSchema::SchemaError.new(nil, "problem", nil)
16
+ assert_equal "problem", e.to_s
17
+ end
18
+ end
@@ -0,0 +1,362 @@
1
+ require "test_helper"
2
+
3
+ require "json_schema"
4
+
5
+ describe JsonSchema::Parser do
6
+ after do
7
+ JsonSchema.configuration.reset!
8
+ end
9
+
10
+ it "parses the basic attributes of a schema" do
11
+ schema = parse
12
+ assert_nil schema.id
13
+ assert_equal "Example API", schema.title
14
+ assert_equal "An example API.", schema.description
15
+ assert_equal ["object"], schema.type
16
+ assert_equal "/", schema.uri
17
+ end
18
+
19
+ it "parses subschemas" do
20
+ schema = parse.definitions["app"]
21
+ assert_nil schema.reference
22
+ assert_equal "App", schema.title
23
+ assert_equal "An app.", schema.description
24
+ assert_equal "schemata/app", schema.id
25
+ assert_equal ["object"], schema.type
26
+ assert_equal "/schemata/app", schema.uri
27
+ refute_nil schema.parent
28
+ end
29
+
30
+ it "parses sub-subschemas" do
31
+ schema = parse.definitions["app"].definitions["name"]
32
+ assert_nil schema.reference
33
+ assert_equal "hello-world", schema.default
34
+ assert_equal "unique name of app", schema.description
35
+ assert_equal ["string"], schema.type
36
+ assert_equal "/schemata/app", schema.uri
37
+ refute_nil schema.parent
38
+ end
39
+
40
+ it "parses references" do
41
+ schema = parse.properties["app"]
42
+ refute_nil schema.reference
43
+ assert_nil schema.reference.uri
44
+ assert_equal "#/definitions/app", schema.reference.pointer
45
+ refute_nil schema.parent
46
+ end
47
+
48
+ it "parses enum validation" do
49
+ schema = parse.definitions["app"].definitions["visibility"]
50
+ assert_equal ["private", "public"], schema.enum
51
+ end
52
+
53
+ it "parses array validations" do
54
+ schema = parse.definitions["app"].definitions["flags"]
55
+ assert_equal(/^[a-z][a-z\-]*[a-z]$/, schema.items.pattern)
56
+ assert_equal 1, schema.min_items
57
+ assert_equal 10, schema.max_items
58
+ assert_equal true, schema.unique_items
59
+ end
60
+
61
+ it "parses array items tuple validation" do
62
+ pointer("#/definitions/app/definitions/flags").merge!(
63
+ "items" => [
64
+ { "enum" => ["bamboo", "cedar"] },
65
+ { "enum" => ["http", "https"] }
66
+ ]
67
+ )
68
+ schema = parse.definitions["app"].definitions["flags"]
69
+ assert_equal ["bamboo", "cedar"], schema.items[0].enum
70
+ assert_equal ["http", "https"], schema.items[1].enum
71
+ end
72
+
73
+ it "parses array additionalItems object validation as boolean" do
74
+ pointer("#/definitions/app/definitions/flags").merge!(
75
+ "additionalItems" => false
76
+ )
77
+ schema = parse.definitions["app"].definitions["flags"]
78
+ assert_equal false, schema.additional_items
79
+ end
80
+
81
+ it "parses array additionalItems object validation as schema" do
82
+ pointer("#/definitions/app/definitions/flags").merge!(
83
+ "additionalItems" => {
84
+ "type" => "boolean"
85
+ }
86
+ )
87
+ schema = parse.definitions["app"].definitions["flags"].additional_items
88
+ assert_equal ["boolean"], schema.type
89
+ end
90
+
91
+ it "parses integer validations" do
92
+ schema = parse.definitions["app"].definitions["id"]
93
+ assert_equal 0, schema.min
94
+ assert_equal true, schema.min_exclusive
95
+ assert_equal 10000, schema.max
96
+ assert_equal false, schema.max_exclusive
97
+ assert_equal 1, schema.multiple_of
98
+ end
99
+
100
+ it "parses number validations" do
101
+ schema = parse.definitions["app"].definitions["cost"]
102
+ assert_equal 0.0, schema.min
103
+ assert_equal false, schema.min_exclusive
104
+ assert_equal 1000.0, schema.max
105
+ assert_equal true, schema.max_exclusive
106
+ assert_equal 0.01, schema.multiple_of
107
+ end
108
+
109
+ it "parses the basic set of object validations" do
110
+ schema = parse.definitions["app"]
111
+ assert_equal 10, schema.max_properties
112
+ assert_equal 1, schema.min_properties
113
+ assert_equal ["name"], schema.required
114
+ end
115
+
116
+ it "parses the additionalProperties object validation as boolean" do
117
+ pointer("#/definitions/app").merge!(
118
+ "additionalProperties" => false
119
+ )
120
+ schema = parse.definitions["app"]
121
+ assert_equal false, schema.additional_properties
122
+ end
123
+
124
+ it "parses the additionalProperties object validation as schema" do
125
+ pointer("#/definitions/app").merge!(
126
+ "additionalProperties" => {
127
+ "type" => "boolean"
128
+ }
129
+ )
130
+ schema = parse.definitions["app"].additional_properties
131
+ assert_equal ["boolean"], schema.type
132
+ end
133
+
134
+ it "parses the dependencies object validation" do
135
+ schema = parse.definitions["app"]
136
+ assert_equal ["ssl"], schema.dependencies["production"]
137
+ assert_equal 20.0, schema.dependencies["ssl"].properties["cost"].min
138
+ end
139
+
140
+ it "parses the patternProperties object validation" do
141
+ schema = parse.definitions["app"].definitions["config_vars"]
142
+ property = schema.pattern_properties.first
143
+ assert_equal(/^\w+$/, property[0])
144
+ assert_equal ["null", "string"], property[1].type
145
+ end
146
+
147
+ it "parses the strictProperties object validation" do
148
+ pointer("#/definitions/app").merge!(
149
+ "strictProperties" => true
150
+ )
151
+ schema = parse.definitions["app"]
152
+ assert_equal true, schema.strict_properties
153
+ end
154
+
155
+ # couldn't think of any non-contrived examples to work with here
156
+ it "parses the basic set of schema validations" do
157
+ schema = parse.definitions["app"].definitions["contrived"]
158
+ assert_equal 2, schema.all_of.count
159
+ assert_equal 2, schema.one_of.count
160
+ assert schema.not
161
+ end
162
+
163
+ it "parses the anyOf schema validation" do
164
+ schema = parse.definitions["app"].definitions["identity"]
165
+ assert_equal 2, schema.any_of.count
166
+ assert_equal "/schemata/app#/definitions/id", schema.any_of[0].reference.to_s
167
+ assert_equal "/schemata/app#/definitions/name", schema.any_of[1].reference.to_s
168
+ end
169
+
170
+ it "parses basic set of string validations" do
171
+ schema = parse.definitions["app"].definitions["name"]
172
+ assert_equal 30, schema.max_length
173
+ assert_equal 3, schema.min_length
174
+ assert_equal(/^[a-z][a-z0-9-]{3,30}$/, schema.pattern)
175
+ end
176
+
177
+ it "parses hypermedia links" do
178
+ pointer("#/definitions/app").merge!(
179
+ "links" => [
180
+ "description" => "Create a new app.",
181
+ "encType" => "application/x-www-form-urlencoded",
182
+ "href" => "/apps",
183
+ "method" => "POST",
184
+ "rel" => "create",
185
+ "mediaType" => "application/json",
186
+ "schema" => {
187
+ "properties" => {
188
+ "name" => {
189
+ "$ref" => "#/definitions/app/definitions/name"
190
+ },
191
+ }
192
+ },
193
+ "targetSchema" => {
194
+ "$ref" => "#/definitions/app"
195
+ }
196
+ ]
197
+ )
198
+ schema = parse.definitions["app"]
199
+ link = schema.links[0]
200
+ assert_equal schema, link.parent
201
+ assert_equal "links/0", link.fragment
202
+ assert_equal "#/definitions/app/links/0", link.pointer
203
+ assert_equal "Create a new app.", link.description
204
+ assert_equal "application/x-www-form-urlencoded", link.enc_type
205
+ assert_equal "/apps", link.href
206
+ assert_equal :post, link.method
207
+ assert_equal "create", link.rel
208
+ assert_equal "application/json", link.media_type
209
+ assert_equal "#/definitions/app/definitions/name",
210
+ link.schema.properties["name"].reference.pointer
211
+ end
212
+
213
+ it "parses hypermedia media" do
214
+ pointer("#/definitions/app/media").merge!(
215
+ "binaryEncoding" => "base64",
216
+ "type" => "image/png"
217
+ )
218
+ schema = parse.definitions["app"]
219
+ assert_equal "base64", schema.media.binary_encoding
220
+ assert_equal "image/png", schema.media.type
221
+ end
222
+
223
+ it "parses hypermedia pathStart" do
224
+ pointer("#/definitions/app").merge!(
225
+ "pathStart" => "/v2"
226
+ )
227
+ schema = parse.definitions["app"]
228
+ assert_equal "/v2", schema.path_start
229
+ end
230
+
231
+ it "parses hypermedia readOnly" do
232
+ pointer("#/definitions/app").merge!(
233
+ "readOnly" => true
234
+ )
235
+ schema = parse.definitions["app"]
236
+ assert_equal true, schema.read_only
237
+ end
238
+
239
+ it "builds appropriate JSON Pointers" do
240
+ schema = parse.definitions["app"].definitions["name"]
241
+ assert_equal "#/definitions/app/definitions/name", schema.pointer
242
+ end
243
+
244
+ it "errors on non-string ids" do
245
+ schema_sample["id"] = 4
246
+ refute parse
247
+ assert_includes error_messages,
248
+ %{4 is not a valid "id", must be a string.}
249
+ assert_includes error_types, :invalid_type
250
+ end
251
+
252
+ it "errors on non-string titles" do
253
+ schema_sample["title"] = 4
254
+ refute parse
255
+ assert_includes error_messages,
256
+ %{4 is not a valid "title", must be a string.}
257
+ assert_includes error_types, :invalid_type
258
+ end
259
+
260
+ it "errors on non-string descriptions" do
261
+ schema_sample["description"] = 4
262
+ refute parse
263
+ assert_includes error_messages,
264
+ %{4 is not a valid "description", must be a string.}
265
+ assert_includes error_types, :invalid_type
266
+ end
267
+
268
+ it "errors on non-array and non-string types" do
269
+ schema_sample["type"] = 4
270
+ refute parse
271
+ assert_includes error_messages,
272
+ %{4 is not a valid "type", must be a array/string.}
273
+ assert_includes error_types, :invalid_type
274
+ end
275
+
276
+ it "errors on unknown types" do
277
+ schema_sample["type"] = ["float", "double"]
278
+ refute parse
279
+ assert_includes error_messages, %{Unknown types: double, float.}
280
+ assert_includes error_types, :unknown_type
281
+ end
282
+
283
+ it "errors on unknown formats" do
284
+ schema_sample["format"] = "obscure-thing"
285
+ refute parse
286
+ assert_includes error_messages, '"obscure-thing" is not a valid format, ' \
287
+ 'must be one of date, date-time, email, ' \
288
+ 'hostname, ipv4, ipv6, regex, uri, ' \
289
+ 'uri-reference, uuid.'
290
+ assert_includes error_types, :unknown_format
291
+ end
292
+
293
+ it "passes for an invalid regex when not asked to check" do
294
+ schema_sample["pattern"] = "\\Ameow"
295
+ assert parse
296
+ end
297
+
298
+ it "errors for an invalid regex when asked to check" do
299
+ require 'ecma-re-validator'
300
+ JsonSchema.configure do |c|
301
+ c.validate_regex_with = :'ecma-re-validator'
302
+ end
303
+ schema_sample["pattern"] = "\\Ameow"
304
+ refute parse
305
+ assert_includes error_messages, '"\\\\Ameow" is not an ECMA-262 regular expression.'
306
+ assert_includes error_types, :regex_failed
307
+ end
308
+
309
+ it "parses custom formats" do
310
+ JsonSchema.configure do |c|
311
+ c.register_format 'the-answer', ->(data) { data.to_i == 42 }
312
+ end
313
+ schema_sample["format"] = "the-answer"
314
+ assert parse
315
+ end
316
+
317
+ it "rejects bad formats even when there are custom formats defined" do
318
+ JsonSchema.configure do |c|
319
+ c.register_format "the-answer", ->(data) { data.to_i == 42 }
320
+ end
321
+ schema_sample["format"] = "not-a-format"
322
+ refute parse
323
+ assert_includes error_messages, '"not-a-format" is not a valid format, ' \
324
+ 'must be one of date, date-time, email, ' \
325
+ 'hostname, ipv4, ipv6, regex, uri, ' \
326
+ 'uri-reference, uuid, the-answer.'
327
+ assert_includes error_types, :unknown_format
328
+ end
329
+
330
+ it "raises an aggregate error with parse!" do
331
+ schema_sample["id"] = 4
332
+
333
+ parser = JsonSchema::Parser.new
334
+
335
+ # don't bother checking the particulars of the error here because we have
336
+ # other tests for that above
337
+ assert_raises JsonSchema::AggregateError do
338
+ parser.parse!(schema_sample)
339
+ end
340
+ end
341
+
342
+ def error_messages
343
+ @parser.errors.map { |e| e.message }
344
+ end
345
+
346
+ def error_types
347
+ @parser.errors.map { |e| e.type }
348
+ end
349
+
350
+ def parse
351
+ @parser = JsonSchema::Parser.new
352
+ @parser.parse(schema_sample)
353
+ end
354
+
355
+ def pointer(path)
356
+ JsonPointer.evaluate(schema_sample, path)
357
+ end
358
+
359
+ def schema_sample
360
+ @schema_sample ||= DataScaffold.schema_sample
361
+ end
362
+ end
@@ -0,0 +1,618 @@
1
+ require "test_helper"
2
+
3
+ require "json_schema"
4
+
5
+ describe JsonSchema::ReferenceExpander do
6
+ it "expands references" do
7
+ expand
8
+ assert_equal [], error_messages
9
+
10
+ # this was always a fully-defined property
11
+ referenced = @schema.definitions["app"]
12
+ # this used to be a $ref
13
+ reference = @schema.properties["app"]
14
+
15
+ assert_equal "#/definitions/app", reference.reference.pointer
16
+ assert_equal referenced.description, reference.description
17
+ assert_equal referenced.id, reference.id
18
+ assert_equal referenced.type, reference.type
19
+ assert_equal referenced.uri, reference.uri
20
+ end
21
+
22
+ it "takes a document store" do
23
+ store = JsonSchema::DocumentStore.new
24
+ expand(store: store)
25
+ assert_equal store, @expander.store
26
+ end
27
+
28
+ it "will expand anyOf" do
29
+ expand
30
+ assert_equal [], error_messages
31
+ schema = @schema.properties["app"].definitions["contrived_plus"]
32
+ assert_equal 3, schema.any_of[0].min_length
33
+ assert_equal 5, schema.any_of[1].min_length
34
+ end
35
+
36
+ it "will expand allOf" do
37
+ expand
38
+ assert_equal [], error_messages
39
+ schema = @schema.properties["app"].definitions["contrived_plus"]
40
+ assert_equal 30, schema.all_of[0].max_length
41
+ assert_equal 3, schema.all_of[1].min_length
42
+ end
43
+
44
+ it "will expand dependencies" do
45
+ expand
46
+ assert_equal [], error_messages
47
+ schema = @schema.properties["app"].dependencies["ssl"].properties["name"]
48
+ assert_equal ["string"], schema.type
49
+ end
50
+
51
+ it "will expand items list schema" do
52
+ pointer("#/definitions/app/definitions/flags").merge!(
53
+ "items" => {
54
+ "$ref" => "#/definitions/app/definitions/name"
55
+ }
56
+ )
57
+ expand
58
+ assert_equal [], error_messages
59
+ schema = @schema.properties["app"].properties["flags"].items
60
+ assert_equal ["string"], schema.type
61
+ end
62
+
63
+ it "will expand items tuple schema" do
64
+ pointer("#/definitions/app/definitions/flags").merge!(
65
+ "items" => [
66
+ { "$ref" => "#/definitions/app/definitions/name" },
67
+ { "$ref" => "#/definitions/app/definitions/owner" }
68
+ ]
69
+ )
70
+ expand
71
+ assert_equal [], error_messages
72
+ schema0 = @schema.properties["app"].properties["flags"].items[0]
73
+ schema1 = @schema.properties["app"].properties["flags"].items[0]
74
+ assert_equal ["string"], schema0.type
75
+ assert_equal ["string"], schema1.type
76
+ end
77
+
78
+ it "will expand oneOf" do
79
+ expand
80
+ assert_equal [], error_messages
81
+ schema = @schema.properties["app"].definitions["contrived_plus"]
82
+ assert_equal(/^(foo|aaa)$/, schema.one_of[0].pattern)
83
+ assert_equal(/^(foo|zzz)$/, schema.one_of[1].pattern)
84
+ end
85
+
86
+ it "will expand not" do
87
+ expand
88
+ assert_equal [], error_messages
89
+ schema = @schema.properties["app"].definitions["contrived_plus"]
90
+ assert_equal(/^$/, schema.not.pattern)
91
+ end
92
+
93
+ it "will expand additionalProperties" do
94
+ pointer("#").merge!(
95
+ "additionalProperties" => { "$ref" => "#" }
96
+ )
97
+ expand
98
+ assert_equal [], error_messages
99
+ schema = @schema.additional_properties
100
+ assert_equal ["object"], schema.type
101
+ end
102
+
103
+ it "will expand patternProperties" do
104
+ expand
105
+ assert_equal [], error_messages
106
+ # value ([1]) of the #first tuple in hash
107
+ schema = @schema.properties["app"].definitions["roles"].
108
+ pattern_properties.first[1]
109
+ assert_equal ["string"], schema.type
110
+ end
111
+
112
+ it "will expand hyperschema link schemas" do
113
+ expand
114
+ assert_equal [], error_messages
115
+ schema = @schema.properties["app"].links[0].schema.properties["name"]
116
+ assert_equal ["string"], schema.type
117
+ end
118
+
119
+ it "will expand hyperschema link targetSchemas" do
120
+ expand
121
+ assert_equal [], error_messages
122
+ schema = @schema.properties["app"].links[0].target_schema.properties["name"]
123
+ assert_equal ["string"], schema.type
124
+ end
125
+
126
+ it "will perform multiple passes to resolve all references" do
127
+ pointer("#/properties").merge!(
128
+ "app0" => { "$ref" => "#/properties/app1" },
129
+ "app1" => { "$ref" => "#/properties/app2" },
130
+ "app2" => { "$ref" => "#/definitions/app" },
131
+ )
132
+ expand
133
+ assert_equal [], error_messages
134
+ schema = @schema.properties["app0"]
135
+ assert_equal ["object"], schema.type
136
+ end
137
+
138
+ it "will resolve circular dependencies" do
139
+ pointer("#/properties").merge!(
140
+ "app" => { "$ref" => "#" }
141
+ )
142
+ expand
143
+ assert_equal [], error_messages
144
+ schema = @schema.properties["app"]
145
+ assert_equal ["object"], schema.type
146
+ end
147
+
148
+ it "builds appropriate JSON Pointers for expanded references" do
149
+ expand
150
+ assert_equal [], error_messages
151
+
152
+ # the *referenced* schema should still have a proper pointer
153
+ schema = @schema.definitions["app"].definitions["name"]
154
+ assert_equal "#/definitions/app/definitions/name", schema.pointer
155
+
156
+ # the *reference* schema should have expanded a pointer
157
+ schema = @schema.properties["app"].properties["name"]
158
+ assert_equal "#/definitions/app/properties/name", schema.pointer
159
+ end
160
+
161
+ # clones are special in that they retain their original pointer despite where
162
+ # they've been nested
163
+ it "builds appropriate JSON Pointers for circular dependencies" do
164
+ pointer("#/properties").merge!(
165
+ "app" => { "$ref" => "#" },
166
+ "app1" => { "$ref" => "#/properties/app"}
167
+ )
168
+ expand
169
+
170
+ # the first self reference has the standard pointer as expected
171
+ schema = @schema.properties["app"]
172
+ assert_equal "#/properties/app", schema.pointer
173
+
174
+ # but diving deeper results in the same pointer again
175
+ schema = schema.properties["app"]
176
+ assert_equal "#/properties/app", schema.pointer
177
+
178
+ schema = @schema.properties["app1"]
179
+ assert_equal "#/properties/app1", schema.pointer
180
+
181
+ schema = schema.properties["app1"]
182
+ assert_equal "#/properties/app1", schema.pointer
183
+ end
184
+
185
+ it "errors on a JSON Pointer that can't be resolved" do
186
+ pointer("#/properties").merge!(
187
+ "app" => { "$ref" => "#/definitions/nope" }
188
+ )
189
+ refute expand
190
+ assert_includes error_messages, %{Couldn't resolve pointer "#/definitions/nope".}
191
+ assert_includes error_types, :unresolved_pointer
192
+ assert_includes error_messages, %{Couldn't resolve references: #/definitions/nope.}
193
+ assert_includes error_types, :unresolved_references
194
+ end
195
+
196
+ it "errors on a URI that can't be resolved" do
197
+ pointer("#/properties").merge!(
198
+ "app" => { "$ref" => "/schemata/user#/definitions/name" }
199
+ )
200
+ refute expand
201
+ assert_includes error_messages,
202
+ %{Couldn't resolve references: /schemata/user#/definitions/name.}
203
+ assert_includes error_types, :unresolved_references
204
+ assert_includes error_messages, %{Couldn't resolve URI: /schemata/user.}
205
+ assert_includes error_types, :unresolved_pointer
206
+ end
207
+
208
+ it "errors on a relative URI that cannot be transformed to an absolute" do
209
+ pointer("#/properties").merge!(
210
+ "app" => { "$ref" => "relative#definitions/name" }
211
+ )
212
+ refute expand
213
+ assert_includes error_messages,
214
+ %{Couldn't resolve references: relative#definitions/name.}
215
+ assert_includes error_types, :unresolved_references
216
+ end
217
+
218
+ it "errors on a reference cycle" do
219
+ pointer("#/properties").merge!(
220
+ "app0" => { "$ref" => "#/properties/app2" },
221
+ "app1" => { "$ref" => "#/properties/app0" },
222
+ "app2" => { "$ref" => "#/properties/app1" },
223
+ )
224
+ refute expand
225
+ properties = "#/properties/app0, #/properties/app1, #/properties/app2"
226
+ assert_includes error_messages, %{Reference loop detected: #{properties}.}
227
+ assert_includes error_types, :loop_detected
228
+ assert_includes error_messages, %{Couldn't resolve references: #{properties}.}
229
+ assert_includes error_types, :unresolved_references
230
+ end
231
+
232
+ it "raises an aggregate error with expand!" do
233
+ pointer("#/properties").merge!(
234
+ "app" => { "$ref" => "#/definitions/nope" }
235
+ )
236
+
237
+ schema = JsonSchema::Parser.new.parse!(schema_sample)
238
+ expander = JsonSchema::ReferenceExpander.new
239
+
240
+ # don't bother checking the particulars of the error here because we have
241
+ # other tests for that above
242
+ assert_raises JsonSchema::AggregateError do
243
+ expander.expand!(schema)
244
+ end
245
+ end
246
+
247
+ it "expands a schema that is just a reference" do
248
+ # First initialize another schema. Give it a fully qualified URI so that we
249
+ # can reference it across schemas.
250
+ schema = JsonSchema::Parser.new.parse!(schema_sample)
251
+ schema.uri = "http://json-schema.org/test"
252
+
253
+ # Initialize a store and add our schema to it.
254
+ store = JsonSchema::DocumentStore.new
255
+ store.add_schema(schema)
256
+
257
+ # Have the parser parse _just_ a reference. It should resolve to a
258
+ # subschema in the schema that we initialized above.
259
+ schema = JsonSchema::Parser.new.parse!(
260
+ { "$ref" => "http://json-schema.org/test#/definitions/app" }
261
+ )
262
+ expander = JsonSchema::ReferenceExpander.new
263
+ expander.expand!(schema, store: store)
264
+
265
+ assert schema.expanded?
266
+ end
267
+
268
+ it "expands a schema with a reference to an external schema in a oneOf array" do
269
+ sample1 = {
270
+ "$schema" => "http://json-schema.org/draft-04/schema#",
271
+ "id" => "http://json-schema.org/draft-04/schema#",
272
+ "definitions" => {
273
+ "schemaArray" => {
274
+ "type" => "array",
275
+ "minItems" => 1,
276
+ "items" => { "$ref" => "#" }
277
+ }
278
+ }
279
+ }
280
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
281
+
282
+ sample2 = {
283
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema#",
284
+ "id" => "http://json-schema.org/draft-04/hyper-schema#",
285
+ "allOf" => [
286
+ {
287
+ "$ref" => "http://json-schema.org/draft-04/schema#"
288
+ }
289
+ ]
290
+ }
291
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
292
+
293
+ store = JsonSchema::DocumentStore.new
294
+ expander = JsonSchema::ReferenceExpander.new
295
+
296
+ store.add_schema(schema1)
297
+ store.add_schema(schema2)
298
+
299
+ expander.expand!(schema2, store: store)
300
+
301
+ assert schema1.expanded?
302
+ assert schema2.expanded?
303
+ end
304
+
305
+ it "expands a schema with a nested reference to an external schema in a oneOf array" do
306
+ sample1 = {
307
+ "$schema" => "http://json-schema.org/draft-04/schema#",
308
+ "id" => "http://json-schema.org/draft-04/schema#",
309
+ "definitions" => {
310
+ "thingy" => {
311
+ "type" => ["string"]
312
+ },
313
+ "schemaArray" => {
314
+ "type" => "array",
315
+ "minItems" => 1,
316
+ "items" => { "$ref" => "#/definitions/thingy" }
317
+ }
318
+ },
319
+ "properties" => {
320
+ "whatsit" => {
321
+ "$ref" => "#/definitions/schemaArray"
322
+ },
323
+ }
324
+ }
325
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
326
+
327
+ sample2 = {
328
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema#",
329
+ "id" => "http://json-schema.org/draft-04/hyper-schema#",
330
+ "allOf" => [
331
+ {
332
+ "$ref" => "http://json-schema.org/draft-04/schema#"
333
+ }
334
+ ]
335
+ }
336
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
337
+
338
+ store = JsonSchema::DocumentStore.new
339
+ expander = JsonSchema::ReferenceExpander.new
340
+
341
+ store.add_schema(schema1)
342
+ store.add_schema(schema2)
343
+
344
+ expander.expand!(schema2, store: store)
345
+
346
+ assert_equal ["string"], schema2.all_of[0].properties["whatsit"].items.type
347
+ end
348
+
349
+ it "expands a schema with a reference to an external schema with a nested external property reference" do
350
+ sample1 = {
351
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
352
+ "type" => "object",
353
+ "properties" => {
354
+ "foo" => {
355
+ "$ref" => "http://json-schema.org/b.json#/definitions/bar"
356
+ }
357
+ }
358
+ }
359
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
360
+ schema1.uri = "http://json-schema.org/a.json"
361
+
362
+ sample2 = {
363
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
364
+ "type" => "object",
365
+ "definitions" => {
366
+ "bar" => {
367
+ "type" => "object",
368
+ "properties" => {
369
+ "omg" => {
370
+ "$ref" => "http://json-schema.org/c.json#/definitions/baz"
371
+ }
372
+ }
373
+ }
374
+ }
375
+ }
376
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
377
+ schema2.uri = "http://json-schema.org/b.json"
378
+
379
+ sample3 = {
380
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
381
+ "type" => "object",
382
+ "definitions" => {
383
+ "baz" => {
384
+ "type" => "string",
385
+ "maxLength" => 3
386
+ }
387
+ }
388
+ }
389
+ schema3 = JsonSchema::Parser.new.parse!(sample3)
390
+ schema3.uri = "http://json-schema.org/c.json"
391
+
392
+ # Initialize a store and add our schema to it.
393
+ store = JsonSchema::DocumentStore.new
394
+ store.add_schema(schema1)
395
+ store.add_schema(schema2)
396
+ store.add_schema(schema3)
397
+
398
+ expander = JsonSchema::ReferenceExpander.new
399
+ expander.expand!(schema1, store: store)
400
+
401
+ assert_equal 3, schema1.properties["foo"].properties["omg"].max_length
402
+ end
403
+
404
+ it "it handles oneOf with nested references to an external schema" do
405
+ sample1 = {
406
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
407
+ "type" => "object",
408
+ "properties" => {
409
+ "foo" => {
410
+ "$ref" => "http://json-schema.org/b.json#"
411
+ }
412
+ }
413
+ }
414
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
415
+ schema1.uri = "http://json-schema.org/a.json"
416
+
417
+ sample2 = {
418
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
419
+ "type" => "object",
420
+ "properties" => {
421
+ "bar" => {
422
+ "oneOf" => [
423
+ {"type" => "null"},
424
+ {"$ref" => "http://json-schema.org/c.json#"}
425
+ ]
426
+ }
427
+ },
428
+ }
429
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
430
+ schema2.uri = "http://json-schema.org/b.json"
431
+
432
+ sample3 = {
433
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
434
+ "type" => "object",
435
+ "properties" => {
436
+ "baz" => {
437
+ "type" => "string",
438
+ "maxLength" => 3
439
+ }
440
+ }
441
+ }
442
+ schema3 = JsonSchema::Parser.new.parse!(sample3)
443
+ schema3.uri = "http://json-schema.org/c.json"
444
+
445
+ # Initialize a store and add our schema to it.
446
+ store = JsonSchema::DocumentStore.new
447
+ store.add_schema(schema1)
448
+ store.add_schema(schema2)
449
+ store.add_schema(schema3)
450
+
451
+ expander = JsonSchema::ReferenceExpander.new
452
+ expander.expand(schema1, store: store)
453
+
454
+ assert_equal 3, schema1.properties["foo"].properties["bar"].one_of[1].properties["baz"].max_length
455
+ end
456
+
457
+ it "does not infinitely recurse when external ref is local to its schema" do
458
+ sample1 = {
459
+ "id" => "http://json-schema.org/draft-04/schema#",
460
+ "$schema" => "http://json-schema.org/draft-04/schema#",
461
+ "properties" => {
462
+ "additionalItems" => {
463
+ "anyOf" => [ { "$ref" => "#" } ]
464
+ }
465
+ }
466
+ }
467
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
468
+ sample2 = {
469
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema#",
470
+ "id" => "http://json-schema.org/draft-04/hyper-schema#",
471
+ "allOf" => [
472
+ { "$ref" => "http://json-schema.org/draft-04/schema#" }
473
+ ]
474
+ }
475
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
476
+
477
+ store = JsonSchema::DocumentStore.new
478
+ expander = JsonSchema::ReferenceExpander.new
479
+
480
+ store.add_schema(schema1)
481
+ store.add_schema(schema2)
482
+
483
+ expander.expand!(schema2, store: store)
484
+
485
+ assert schema1.expanded?
486
+ assert schema2.expanded?
487
+ end
488
+
489
+ it "it handles oneOf with nested references to a local schema" do
490
+ sample1 = {
491
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
492
+ "type" => "object",
493
+ "properties" => {
494
+ "foo" => {
495
+ "$ref" => "http://json-schema.org/b.json#"
496
+ }
497
+ }
498
+ }
499
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
500
+ schema1.uri = "http://json-schema.org/a.json"
501
+
502
+ sample2 = {
503
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
504
+ "type" => "object",
505
+ "definitions" => {
506
+ "baz" => {
507
+ "type" => "string",
508
+ "maxLength" => 3
509
+ }
510
+ },
511
+ "properties" => {
512
+ "bar" => {
513
+ "oneOf" => [
514
+ {"type" => "null"},
515
+ {"$ref" => "#/definitions/baz"}
516
+ ]
517
+ }
518
+ },
519
+ }
520
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
521
+ schema2.uri = "http://json-schema.org/b.json"
522
+
523
+ # Initialize a store and add our schema to it.
524
+ store = JsonSchema::DocumentStore.new
525
+ store.add_schema(schema1)
526
+ store.add_schema(schema2)
527
+
528
+ expander = JsonSchema::ReferenceExpander.new
529
+ expander.expand(schema1, store: store)
530
+
531
+ assert_equal 3, schema1.properties["foo"].properties["bar"].one_of[1].max_length
532
+ end
533
+
534
+ it "expands a schema with a reference to an external schema with a nested local property reference" do
535
+ sample1 = {
536
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
537
+ "type" => "object",
538
+ "properties" => {
539
+ "foo" => {
540
+ "$ref" => "http://json-schema.org/b.json#/definitions/bar"
541
+ },
542
+ "foo2" => {
543
+ "$ref" => "http://json-schema.org/b.json#/definitions/baz"
544
+ }
545
+ }
546
+ }
547
+ schema1 = JsonSchema::Parser.new.parse!(sample1)
548
+ schema1.uri = "http://json-schema.org/a.json"
549
+
550
+ sample2 = {
551
+ "$schema" => "http://json-schema.org/draft-04/hyper-schema",
552
+ "type" => "object",
553
+ "definitions" => {
554
+ "bar" => {
555
+ "type" => "object",
556
+ "properties" => {
557
+ "omg" => {
558
+ "$ref" => "#/definitions/baz"
559
+ }
560
+ }
561
+ },
562
+ "baz" => {
563
+ "type" => "string",
564
+ "maxLength" => 3
565
+ }
566
+ }
567
+ }
568
+ schema2 = JsonSchema::Parser.new.parse!(sample2)
569
+ schema2.uri = "http://json-schema.org/b.json"
570
+
571
+ # Initialize a store and add our schema to it.
572
+ store = JsonSchema::DocumentStore.new
573
+ store.add_schema(schema1)
574
+ store.add_schema(schema2)
575
+
576
+ expander = JsonSchema::ReferenceExpander.new
577
+ expander.expand!(schema1, store: store)
578
+
579
+ # These both point to the same definition, 'baz', but
580
+ # 'foo' has a level of indirection.
581
+ assert_equal 3, schema1.properties["foo2"].max_length
582
+ assert_equal 3, schema1.properties["foo"].properties["omg"].max_length
583
+ end
584
+
585
+ it "expands a reference to a link" do
586
+ pointer("#/properties").merge!(
587
+ "link" => { "$ref" => "#/links/0" }
588
+ )
589
+ assert expand
590
+
591
+ referenced = @schema.links[0]
592
+ reference = @schema.properties["link"]
593
+
594
+ assert_equal reference.href, referenced.href
595
+ end
596
+
597
+ def error_messages
598
+ @expander.errors.map { |e| e.message }
599
+ end
600
+
601
+ def error_types
602
+ @expander.errors.map { |e| e.type }
603
+ end
604
+
605
+ def pointer(path)
606
+ JsonPointer.evaluate(schema_sample, path)
607
+ end
608
+
609
+ def schema_sample
610
+ @schema_sample ||= DataScaffold.schema_sample
611
+ end
612
+
613
+ def expand(options = {})
614
+ @schema = JsonSchema::Parser.new.parse!(schema_sample)
615
+ @expander = JsonSchema::ReferenceExpander.new
616
+ @expander.expand(@schema, options)
617
+ end
618
+ end