dato_json_schema 0.20.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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