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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +75 -0
- data/bin/validate-schema +40 -0
- data/lib/commands/validate_schema.rb +130 -0
- data/lib/json_pointer.rb +7 -0
- data/lib/json_pointer/evaluator.rb +80 -0
- data/lib/json_reference.rb +58 -0
- data/lib/json_schema.rb +31 -0
- data/lib/json_schema/attributes.rb +117 -0
- data/lib/json_schema/configuration.rb +28 -0
- data/lib/json_schema/document_store.rb +30 -0
- data/lib/json_schema/error.rb +85 -0
- data/lib/json_schema/parser.rb +390 -0
- data/lib/json_schema/reference_expander.rb +364 -0
- data/lib/json_schema/schema.rb +295 -0
- data/lib/json_schema/validator.rb +606 -0
- data/schemas/hyper-schema.json +168 -0
- data/schemas/schema.json +150 -0
- data/test/bin_test.rb +19 -0
- data/test/commands/validate_schema_test.rb +121 -0
- data/test/data_scaffold.rb +241 -0
- data/test/json_pointer/evaluator_test.rb +69 -0
- data/test/json_reference/reference_test.rb +45 -0
- data/test/json_schema/attribute_test.rb +121 -0
- data/test/json_schema/document_store_test.rb +42 -0
- data/test/json_schema/error_test.rb +18 -0
- data/test/json_schema/parser_test.rb +362 -0
- data/test/json_schema/reference_expander_test.rb +618 -0
- data/test/json_schema/schema_test.rb +46 -0
- data/test/json_schema/validator_test.rb +1078 -0
- data/test/json_schema_test.rb +46 -0
- data/test/test_helper.rb +17 -0
- metadata +77 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "json_schema"
|
4
|
+
|
5
|
+
describe JsonSchema::Schema do
|
6
|
+
it "allows schema attribute access with #[]" do
|
7
|
+
schema = JsonSchema::Schema.new
|
8
|
+
schema.properties = { "foo" => nil }
|
9
|
+
assert_equal({ "foo" => nil }, schema[:properties])
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows schema attribute access with #[] and overridden name" do
|
13
|
+
schema = JsonSchema::Schema.new
|
14
|
+
schema.additional_properties = { "foo" => nil }
|
15
|
+
assert_equal({ "foo" => nil }, schema[:additionalProperties])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "allows schema attribute access with #[] as string" do
|
19
|
+
schema = JsonSchema::Schema.new
|
20
|
+
schema.properties = { "foo" => nil }
|
21
|
+
assert_equal({ "foo" => nil }, schema["properties"])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises if attempting to access #[] with bad method" do
|
25
|
+
schema = JsonSchema::Schema.new
|
26
|
+
assert_raises NoMethodError do
|
27
|
+
schema[:wat]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "raises if attempting to access #[] with non-schema attribute" do
|
32
|
+
schema = JsonSchema::Schema.new
|
33
|
+
assert_raises NoMethodError do
|
34
|
+
schema[:expanded]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "updates type_parsed when type is changed" do
|
39
|
+
schema = JsonSchema::Schema.new
|
40
|
+
schema.type = ["integer"]
|
41
|
+
assert_equal [Integer], schema.type_parsed
|
42
|
+
|
43
|
+
schema.type = ["string"]
|
44
|
+
assert_equal [String], schema.type_parsed
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,1078 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "json_schema"
|
4
|
+
|
5
|
+
describe JsonSchema::Validator do
|
6
|
+
after do
|
7
|
+
JsonSchema.configuration.reset!
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can find data valid" do
|
11
|
+
assert_valid
|
12
|
+
end
|
13
|
+
|
14
|
+
it "validates enum successfully" do
|
15
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
16
|
+
"enum" => ["private", "public"]
|
17
|
+
)
|
18
|
+
data_sample["visibility"] = "public"
|
19
|
+
assert_valid
|
20
|
+
end
|
21
|
+
|
22
|
+
it "validates enum unsuccessfully" do
|
23
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
24
|
+
"enum" => ["private", "public"]
|
25
|
+
)
|
26
|
+
data_sample["visibility"] = "personal"
|
27
|
+
refute_valid
|
28
|
+
assert_includes error_messages,
|
29
|
+
%{personal is not a member of ["private", "public"].}
|
30
|
+
assert_includes error_types, :invalid_type
|
31
|
+
end
|
32
|
+
|
33
|
+
it "validates type successfully" do
|
34
|
+
pointer("#/definitions/app").merge!(
|
35
|
+
"type" => ["object"]
|
36
|
+
)
|
37
|
+
@data_sample = { "name" => "cloudnasium" }
|
38
|
+
assert_valid
|
39
|
+
end
|
40
|
+
|
41
|
+
it "validates sub-type successfully" do
|
42
|
+
pointer("#/definitions/app").merge!(
|
43
|
+
"type" => ["object"]
|
44
|
+
)
|
45
|
+
class SomeClass < Hash; end
|
46
|
+
@data_sample = SomeClass.new
|
47
|
+
@data_sample["name"] = "yayrails"
|
48
|
+
assert_valid
|
49
|
+
end
|
50
|
+
|
51
|
+
it "validates type unsuccessfully" do
|
52
|
+
pointer("#/definitions/app").merge!(
|
53
|
+
"type" => ["object"]
|
54
|
+
)
|
55
|
+
@data_sample = 4
|
56
|
+
refute_valid
|
57
|
+
assert_includes error_messages, %{For 'definitions/app', 4 is not an object.}
|
58
|
+
assert_includes error_types, :invalid_type
|
59
|
+
assert_includes error_data, 4
|
60
|
+
end
|
61
|
+
|
62
|
+
it "provides accurate error messages for multiple type errors" do
|
63
|
+
pointer("#/definitions/app").merge!(
|
64
|
+
"type" => ["string"]
|
65
|
+
)
|
66
|
+
@data_sample = 4
|
67
|
+
refute_valid
|
68
|
+
assert_includes error_messages, %{For 'definitions/app', 4 is not a string.}
|
69
|
+
assert_includes error_types, :invalid_type
|
70
|
+
|
71
|
+
pointer("#/definitions/app").merge!(
|
72
|
+
"type" => ["string", "null"]
|
73
|
+
)
|
74
|
+
@data_sample = 4
|
75
|
+
refute_valid
|
76
|
+
assert_includes error_messages, %{For 'definitions/app', 4 is not a string or null.}
|
77
|
+
assert_includes error_types, :invalid_type
|
78
|
+
|
79
|
+
pointer("#/definitions/app").merge!(
|
80
|
+
"type" => ["object", "null", "string"]
|
81
|
+
)
|
82
|
+
@data_sample = 4
|
83
|
+
refute_valid
|
84
|
+
assert_includes error_messages, %{For 'definitions/app', 4 is not an object, null, or string.}
|
85
|
+
assert_includes error_types, :invalid_type
|
86
|
+
end
|
87
|
+
|
88
|
+
it "validates items with list successfully" do
|
89
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
90
|
+
"items" => {
|
91
|
+
"pattern" => "^[a-z][a-z\\-]*[a-z]$"
|
92
|
+
}
|
93
|
+
)
|
94
|
+
data_sample["flags"] = ["websockets"]
|
95
|
+
assert_valid
|
96
|
+
end
|
97
|
+
|
98
|
+
it "validates items with list unsuccessfully" do
|
99
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
100
|
+
"items" => {
|
101
|
+
"pattern" => "^[a-z][a-z\\-]*[a-z]$"
|
102
|
+
}
|
103
|
+
)
|
104
|
+
data_sample["flags"] = ["1337"]
|
105
|
+
refute_valid
|
106
|
+
assert_includes error_messages,
|
107
|
+
%{1337 does not match /^[a-z][a-z\\-]*[a-z]$/.}
|
108
|
+
assert_includes error_types, :pattern_failed
|
109
|
+
assert_includes error_data, "1337"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "validates items with tuple successfully" do
|
113
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
114
|
+
"items" => [
|
115
|
+
{ "enum" => ["bamboo", "cedar"] },
|
116
|
+
{ "enum" => ["http", "https"] }
|
117
|
+
]
|
118
|
+
)
|
119
|
+
data_sample["flags"] = ["cedar", "https"]
|
120
|
+
assert_valid
|
121
|
+
end
|
122
|
+
|
123
|
+
it "validates items with tuple with additionalItems boolean successfully" do
|
124
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
125
|
+
"additionalItems" => true,
|
126
|
+
"items" => [
|
127
|
+
{ "enum" => ["bamboo", "cedar"] },
|
128
|
+
{ "enum" => ["http", "https"] }
|
129
|
+
]
|
130
|
+
)
|
131
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
132
|
+
assert_valid
|
133
|
+
end
|
134
|
+
|
135
|
+
it "validates items with tuple with additionalItems boolean unsuccessfully" do
|
136
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
137
|
+
"additionalItems" => false,
|
138
|
+
"items" => [
|
139
|
+
{ "enum" => ["bamboo", "cedar"] },
|
140
|
+
{ "enum" => ["http", "https"] }
|
141
|
+
]
|
142
|
+
)
|
143
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
144
|
+
refute_valid
|
145
|
+
assert_includes error_messages, %{No more than 2 items are allowed; 3 were supplied.}
|
146
|
+
assert_includes error_types, :max_items_failed
|
147
|
+
assert_includes error_data, ["cedar", "https", "websockets"]
|
148
|
+
end
|
149
|
+
|
150
|
+
it "validates items with tuple with additionalItems schema successfully" do
|
151
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
152
|
+
"additionalItems" => { "enum" => [ "foo", "websockets" ] },
|
153
|
+
"items" => [
|
154
|
+
{ "enum" => ["bamboo", "cedar"] },
|
155
|
+
{ "enum" => ["http", "https"] }
|
156
|
+
]
|
157
|
+
)
|
158
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
159
|
+
assert_valid
|
160
|
+
end
|
161
|
+
|
162
|
+
it "validates items with tuple with additionalItems schema unsuccessfully for non-conforming additional item" do
|
163
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
164
|
+
"additionalItems" => { "enum" => [ "foo", "bar" ] },
|
165
|
+
"items" => [
|
166
|
+
{ "enum" => ["bamboo", "cedar"] },
|
167
|
+
{ "enum" => ["http", "https"] }
|
168
|
+
]
|
169
|
+
)
|
170
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
171
|
+
refute_valid
|
172
|
+
assert_includes error_messages,
|
173
|
+
%{websockets is not a member of ["foo", "bar"].}
|
174
|
+
assert_includes error_types, :invalid_type
|
175
|
+
assert_includes error_data, "websockets"
|
176
|
+
end
|
177
|
+
|
178
|
+
it "validates items with tuple with additionalItems schema unsuccessfully with multiple failures" do
|
179
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
180
|
+
"additionalItems" => { "enum" => [ "foo", "bar" ] },
|
181
|
+
"items" => [
|
182
|
+
{ "enum" => ["bamboo", "cedar"] },
|
183
|
+
{ "enum" => ["http", "https"] }
|
184
|
+
]
|
185
|
+
)
|
186
|
+
data_sample["flags"] = ["cedar", "https", "websockets", "1337"]
|
187
|
+
refute_valid
|
188
|
+
assert_includes error_messages,
|
189
|
+
%{websockets is not a member of ["foo", "bar"].}
|
190
|
+
assert_includes error_types, :invalid_type
|
191
|
+
assert_includes error_data, "websockets"
|
192
|
+
assert_includes error_messages,
|
193
|
+
%{1337 is not a member of ["foo", "bar"].}
|
194
|
+
assert_includes error_types, :invalid_type
|
195
|
+
assert_includes error_data, "1337"
|
196
|
+
end
|
197
|
+
|
198
|
+
it "validates items with tuple with additionalItems schema unsuccessfully with non-conforming items and additional items" do
|
199
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
200
|
+
"additionalItems" => { "enum" => [ "foo", "bar" ] },
|
201
|
+
"items" => [
|
202
|
+
{ "enum" => ["bamboo", "cedar"] },
|
203
|
+
{ "enum" => ["http", "https"] }
|
204
|
+
]
|
205
|
+
)
|
206
|
+
data_sample["flags"] = ["cedar", "1337", "websockets"]
|
207
|
+
refute_valid
|
208
|
+
assert_includes error_messages,
|
209
|
+
%{websockets is not a member of ["foo", "bar"].}
|
210
|
+
assert_includes error_types, :invalid_type
|
211
|
+
assert_includes error_data, "websockets"
|
212
|
+
assert_includes error_messages,
|
213
|
+
%{1337 is not a member of ["http", "https"].}
|
214
|
+
assert_includes error_types, :invalid_type
|
215
|
+
assert_includes error_data, "1337"
|
216
|
+
end
|
217
|
+
|
218
|
+
it "validates items with tuple unsuccessfully for not enough items" do
|
219
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
220
|
+
"items" => [
|
221
|
+
{ "enum" => ["bamboo", "cedar"] },
|
222
|
+
{ "enum" => ["http", "https"] }
|
223
|
+
]
|
224
|
+
)
|
225
|
+
data_sample["flags"] = ["cedar"]
|
226
|
+
refute_valid
|
227
|
+
assert_includes error_messages,
|
228
|
+
%{2 items required; only 1 was supplied.}
|
229
|
+
assert_includes error_types, :min_items_failed
|
230
|
+
assert_includes error_data, ["cedar"]
|
231
|
+
end
|
232
|
+
|
233
|
+
it "validates items with tuple unsuccessfully for too many items" do
|
234
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
235
|
+
"additionalItems" => false,
|
236
|
+
"items" => [
|
237
|
+
{ "enum" => ["bamboo", "cedar"] },
|
238
|
+
{ "enum" => ["http", "https"] }
|
239
|
+
]
|
240
|
+
)
|
241
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
242
|
+
refute_valid
|
243
|
+
assert_includes error_messages,
|
244
|
+
%{No more than 2 items are allowed; 3 were supplied.}
|
245
|
+
assert_includes error_types, :max_items_failed
|
246
|
+
assert_includes error_data, ["cedar", "https", "websockets"]
|
247
|
+
end
|
248
|
+
|
249
|
+
it "validates items with tuple unsuccessfully for non-conforming items" do
|
250
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
251
|
+
"additionalItems" => false,
|
252
|
+
"items" => [
|
253
|
+
{ "enum" => ["bamboo", "cedar"] },
|
254
|
+
{ "enum" => ["http", "https"] }
|
255
|
+
]
|
256
|
+
)
|
257
|
+
data_sample["flags"] = ["cedar", "1337"]
|
258
|
+
refute_valid
|
259
|
+
assert_includes error_messages,
|
260
|
+
%{1337 is not a member of ["http", "https"].}
|
261
|
+
assert_includes error_types, :invalid_type
|
262
|
+
assert_includes error_data, "1337"
|
263
|
+
end
|
264
|
+
|
265
|
+
it "validates maxItems successfully" do
|
266
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
267
|
+
"maxItems" => 10
|
268
|
+
)
|
269
|
+
data_sample["flags"] = (0...10).to_a
|
270
|
+
assert_valid
|
271
|
+
end
|
272
|
+
|
273
|
+
it "validates maxItems unsuccessfully" do
|
274
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
275
|
+
"maxItems" => 10
|
276
|
+
)
|
277
|
+
data_sample["flags"] = (0...11).to_a
|
278
|
+
refute_valid
|
279
|
+
assert_includes error_messages,
|
280
|
+
%{No more than 10 items are allowed; 11 were supplied.}
|
281
|
+
assert_includes error_types, :max_items_failed
|
282
|
+
assert_includes error_data, (0...11).to_a
|
283
|
+
end
|
284
|
+
|
285
|
+
it "validates minItems successfully" do
|
286
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
287
|
+
"minItems" => 1
|
288
|
+
)
|
289
|
+
data_sample["flags"] = ["websockets"]
|
290
|
+
assert_valid
|
291
|
+
end
|
292
|
+
|
293
|
+
it "validates minItems unsuccessfully" do
|
294
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
295
|
+
"minItems" => 1
|
296
|
+
)
|
297
|
+
data_sample["flags"] = []
|
298
|
+
refute_valid
|
299
|
+
assert_includes error_messages, %{1 item required; only 0 were supplied.}
|
300
|
+
assert_includes error_types, :min_items_failed
|
301
|
+
assert_includes error_data, []
|
302
|
+
end
|
303
|
+
|
304
|
+
it "validates uniqueItems successfully" do
|
305
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
306
|
+
"uniqueItems" => true
|
307
|
+
)
|
308
|
+
data_sample["flags"] = ["websockets"]
|
309
|
+
assert_valid
|
310
|
+
end
|
311
|
+
|
312
|
+
it "validates uniqueItems unsuccessfully" do
|
313
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
314
|
+
"uniqueItems" => true
|
315
|
+
)
|
316
|
+
data_sample["flags"] = ["websockets", "websockets"]
|
317
|
+
refute_valid
|
318
|
+
assert_includes error_messages, %{Duplicate items are not allowed.}
|
319
|
+
assert_includes error_types, :unique_items_failed
|
320
|
+
assert_includes error_data, ["websockets", "websockets"]
|
321
|
+
end
|
322
|
+
|
323
|
+
it "validates maximum for an integer with exclusiveMaximum false" do
|
324
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
325
|
+
"exclusiveMaximum" => false,
|
326
|
+
"maximum" => 10
|
327
|
+
)
|
328
|
+
data_sample["id"] = 11
|
329
|
+
refute_valid
|
330
|
+
assert_includes error_messages, %{11 must be less than or equal to 10.}
|
331
|
+
assert_includes error_types, :max_failed
|
332
|
+
assert_includes error_data, 11
|
333
|
+
end
|
334
|
+
|
335
|
+
it "validates maximum for an integer with exclusiveMaximum true" do
|
336
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
337
|
+
"exclusiveMaximum" => true,
|
338
|
+
"maximum" => 10
|
339
|
+
)
|
340
|
+
data_sample["id"] = 10
|
341
|
+
refute_valid
|
342
|
+
assert_includes error_messages, %{10 must be less than 10.}
|
343
|
+
assert_includes error_types, :max_failed
|
344
|
+
end
|
345
|
+
|
346
|
+
it "validates maximum for a number with exclusiveMaximum false" do
|
347
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
348
|
+
"exclusiveMaximum" => false,
|
349
|
+
"maximum" => 10.0
|
350
|
+
)
|
351
|
+
data_sample["cost"] = 10.1
|
352
|
+
refute_valid
|
353
|
+
assert_includes error_messages, %{10.1 must be less than or equal to 10.0.}
|
354
|
+
assert_includes error_types, :max_failed
|
355
|
+
end
|
356
|
+
|
357
|
+
it "validates maximum for a number with exclusiveMaximum true" do
|
358
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
359
|
+
"exclusiveMaximum" => true,
|
360
|
+
"maximum" => 10.0
|
361
|
+
)
|
362
|
+
data_sample["cost"] = 10.0
|
363
|
+
refute_valid
|
364
|
+
assert_includes error_messages, %{10.0 must be less than 10.0.}
|
365
|
+
assert_includes error_types, :max_failed
|
366
|
+
end
|
367
|
+
|
368
|
+
it "validates minimum for an integer with exclusiveMaximum false" do
|
369
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
370
|
+
"exclusiveMinimum" => false,
|
371
|
+
"minimum" => 1
|
372
|
+
)
|
373
|
+
data_sample["id"] = 0
|
374
|
+
refute_valid
|
375
|
+
assert_includes error_messages, %{0 must be greater than or equal to 1.}
|
376
|
+
assert_includes error_types, :min_failed
|
377
|
+
assert_includes error_data, 0
|
378
|
+
end
|
379
|
+
|
380
|
+
it "validates minimum for an integer with exclusiveMaximum true" do
|
381
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
382
|
+
"exclusiveMinimum" => true,
|
383
|
+
"minimum" => 1
|
384
|
+
)
|
385
|
+
data_sample["id"] = 1
|
386
|
+
refute_valid
|
387
|
+
assert_includes error_messages, %{1 must be greater than 1.}
|
388
|
+
end
|
389
|
+
|
390
|
+
it "validates minimum for a number with exclusiveMaximum false" do
|
391
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
392
|
+
"exclusiveMinimum" => false,
|
393
|
+
"minimum" => 0.0
|
394
|
+
)
|
395
|
+
data_sample["cost"] = -0.01
|
396
|
+
refute_valid
|
397
|
+
assert_includes error_messages,
|
398
|
+
%{-0.01 must be greater than or equal to 0.0.}
|
399
|
+
assert_includes error_types, :min_failed
|
400
|
+
end
|
401
|
+
|
402
|
+
it "validates minimum for a number with exclusiveMaximum true" do
|
403
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
404
|
+
"exclusiveMinimum" => true,
|
405
|
+
"minimum" => 0.0
|
406
|
+
)
|
407
|
+
data_sample["cost"] = 0.0
|
408
|
+
refute_valid
|
409
|
+
assert_includes error_messages, %{0.0 must be greater than 0.0.}
|
410
|
+
assert_includes error_types, :min_failed
|
411
|
+
end
|
412
|
+
|
413
|
+
it "validates multipleOf for an integer" do
|
414
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
415
|
+
"multipleOf" => 2
|
416
|
+
)
|
417
|
+
data_sample["id"] = 1
|
418
|
+
refute_valid
|
419
|
+
assert_includes error_messages, %{1 is not a multiple of 2.}
|
420
|
+
assert_includes error_types, :multiple_of_failed
|
421
|
+
assert_includes error_data, 1
|
422
|
+
end
|
423
|
+
|
424
|
+
it "validates multipleOf for a number" do
|
425
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
426
|
+
"multipleOf" => 0.01
|
427
|
+
)
|
428
|
+
data_sample["cost"] = 0.005
|
429
|
+
refute_valid
|
430
|
+
assert_includes error_messages, %{0.005 is not a multiple of 0.01.}
|
431
|
+
assert_includes error_types, :multiple_of_failed
|
432
|
+
end
|
433
|
+
|
434
|
+
it "validates additionalProperties boolean successfully" do
|
435
|
+
pointer("#/definitions/app").merge!(
|
436
|
+
"additionalProperties" => true
|
437
|
+
)
|
438
|
+
data_sample["foo"] = "bar"
|
439
|
+
assert_valid
|
440
|
+
end
|
441
|
+
|
442
|
+
it "validates additionalProperties boolean unsuccessfully" do
|
443
|
+
pointer("#/definitions/app").merge!(
|
444
|
+
"additionalProperties" => false,
|
445
|
+
"patternProperties" => {
|
446
|
+
"^matches" => {}
|
447
|
+
}
|
448
|
+
)
|
449
|
+
data_sample["foo"] = "bar"
|
450
|
+
data_sample["matches_pattern"] = "yes!"
|
451
|
+
refute_valid
|
452
|
+
assert_includes error_messages, %{"foo" is not a permitted key.}
|
453
|
+
assert_includes error_types, :invalid_keys
|
454
|
+
end
|
455
|
+
|
456
|
+
it "validates additionalProperties boolean unsuccessfully with multiple failures" do
|
457
|
+
pointer("#/definitions/app").merge!(
|
458
|
+
"additionalProperties" => false,
|
459
|
+
"patternProperties" => {
|
460
|
+
"^matches" => {}
|
461
|
+
}
|
462
|
+
)
|
463
|
+
data_sample["foo"] = "bar"
|
464
|
+
data_sample["baz"] = "blah"
|
465
|
+
data_sample["matches_pattern"] = "yes!"
|
466
|
+
refute_valid
|
467
|
+
assert_includes error_messages, %{"baz", "foo" are not permitted keys.}
|
468
|
+
assert_includes error_types, :invalid_keys
|
469
|
+
end
|
470
|
+
|
471
|
+
it "validates additionalProperties schema successfully" do
|
472
|
+
pointer("#/definitions/app").merge!(
|
473
|
+
"additionalProperties" => {
|
474
|
+
"type" => ["boolean"]
|
475
|
+
}
|
476
|
+
)
|
477
|
+
data_sample["foo"] = true
|
478
|
+
assert_valid
|
479
|
+
end
|
480
|
+
|
481
|
+
it "validates additionalProperties schema unsuccessfully" do
|
482
|
+
pointer("#/definitions/app").merge!(
|
483
|
+
"additionalProperties" => {
|
484
|
+
"type" => ["boolean"]
|
485
|
+
},
|
486
|
+
"patternProperties" => {
|
487
|
+
"^matches" => {}
|
488
|
+
}
|
489
|
+
)
|
490
|
+
data_sample["foo"] = 4
|
491
|
+
data_sample["matches_pattern"] = "yes!"
|
492
|
+
refute_valid
|
493
|
+
assert_includes error_messages, %{For 'additionalProperties', 4 is not a boolean.}
|
494
|
+
assert_includes error_types, :invalid_type
|
495
|
+
end
|
496
|
+
|
497
|
+
it "validates simple dependencies" do
|
498
|
+
pointer("#/definitions/app/dependencies").merge!(
|
499
|
+
"production" => "ssl"
|
500
|
+
)
|
501
|
+
data_sample["production"] = true
|
502
|
+
refute_valid
|
503
|
+
assert_includes error_messages,
|
504
|
+
%{"ssl" wasn't supplied.}
|
505
|
+
end
|
506
|
+
|
507
|
+
it "validates schema dependencies" do
|
508
|
+
pointer("#/definitions/app/dependencies").merge!(
|
509
|
+
"ssl" => {
|
510
|
+
"properties" => {
|
511
|
+
"cost" => {
|
512
|
+
"minimum" => 20.0,
|
513
|
+
}
|
514
|
+
}
|
515
|
+
}
|
516
|
+
)
|
517
|
+
data_sample["cost"] = 10.0
|
518
|
+
data_sample["ssl"] = true
|
519
|
+
refute_valid
|
520
|
+
assert_includes error_messages, %{10.0 must be greater than or equal to 20.0.}
|
521
|
+
assert_includes error_types, :min_failed
|
522
|
+
end
|
523
|
+
|
524
|
+
it "validates maxProperties" do
|
525
|
+
pointer("#/definitions/app").merge!(
|
526
|
+
"maxProperties" => 0
|
527
|
+
)
|
528
|
+
data_sample["name"] = "cloudnasium"
|
529
|
+
refute_valid
|
530
|
+
assert_includes error_messages, %{No more than 0 properties are allowed; 1 was supplied.}
|
531
|
+
assert_includes error_types, :max_properties_failed
|
532
|
+
assert_includes error_data, { "name" => "cloudnasium" }
|
533
|
+
end
|
534
|
+
|
535
|
+
it "validates minProperties" do
|
536
|
+
pointer("#/definitions/app").merge!(
|
537
|
+
"minProperties" => 2
|
538
|
+
)
|
539
|
+
data_sample["name"] = "cloudnasium"
|
540
|
+
refute_valid
|
541
|
+
assert_includes error_messages, %{At least 2 properties are required; 1 was supplied.}
|
542
|
+
assert_includes error_types, :min_properties_failed
|
543
|
+
assert_includes error_data, { "name" => "cloudnasium" }
|
544
|
+
end
|
545
|
+
|
546
|
+
it "validates patternProperties" do
|
547
|
+
pointer("#/definitions/app/definitions/config_vars").merge!(
|
548
|
+
"patternProperties" => {
|
549
|
+
"^\\w+$" => {
|
550
|
+
"type" => ["null", "string"]
|
551
|
+
}
|
552
|
+
}
|
553
|
+
)
|
554
|
+
data_sample["config_vars"] = {
|
555
|
+
"" => 123,
|
556
|
+
"KEY" => 456
|
557
|
+
}
|
558
|
+
refute_valid
|
559
|
+
assert_includes error_messages, %{For 'definitions/config_vars', 456 is not a null or string.}
|
560
|
+
assert_includes error_types, :invalid_type
|
561
|
+
end
|
562
|
+
|
563
|
+
it "validates patternProperties with missing parent" do
|
564
|
+
data_sample["S_0"] = 123
|
565
|
+
|
566
|
+
refute validate_parentless_pattern
|
567
|
+
assert_includes error_messages, %{For 'patternProperties/^S_', 123 is not a string.}
|
568
|
+
assert_includes error_types, :invalid_type
|
569
|
+
end
|
570
|
+
|
571
|
+
it "validates required" do
|
572
|
+
pointer("#/definitions/app/dependencies").merge!(
|
573
|
+
"required" => ["name"]
|
574
|
+
)
|
575
|
+
data_sample.delete("name")
|
576
|
+
refute_valid
|
577
|
+
assert_includes error_messages, %{"name" wasn't supplied.}
|
578
|
+
assert_includes error_types, :required_failed
|
579
|
+
assert_includes error_data, ["name"]
|
580
|
+
end
|
581
|
+
|
582
|
+
it "validates strictProperties successfully" do
|
583
|
+
pointer("#/definitions/app").merge!(
|
584
|
+
"strictProperties" => false
|
585
|
+
)
|
586
|
+
assert_valid
|
587
|
+
end
|
588
|
+
|
589
|
+
it "validates strictProperties unsuccessfully" do
|
590
|
+
pointer("#/definitions/app").merge!(
|
591
|
+
"patternProperties" => {
|
592
|
+
"^matches" => {}
|
593
|
+
},
|
594
|
+
"strictProperties" => true
|
595
|
+
)
|
596
|
+
data_sample["extra_key"] = "value"
|
597
|
+
data_sample["matches_pattern"] = "yes!"
|
598
|
+
refute_valid
|
599
|
+
missing = @schema.properties.keys.sort - ["name"]
|
600
|
+
assert_includes error_messages, %{"#{missing.join('", "')}" weren't supplied.}
|
601
|
+
assert_includes error_messages, %{"extra_key" is not a permitted key.}
|
602
|
+
assert_includes error_types, :invalid_keys
|
603
|
+
end
|
604
|
+
|
605
|
+
it "validates allOf" do
|
606
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
607
|
+
"allOf" => [
|
608
|
+
{ "maxLength" => 30 },
|
609
|
+
{ "minLength" => 3 }
|
610
|
+
]
|
611
|
+
)
|
612
|
+
data_sample["contrived"] = "ab"
|
613
|
+
refute_valid
|
614
|
+
assert_includes error_messages, %{Not all subschemas of "allOf" matched.}
|
615
|
+
assert_includes error_types, :all_of_failed
|
616
|
+
end
|
617
|
+
|
618
|
+
it "includes the failing condition when validating allOf" do
|
619
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
620
|
+
"allOf" => [
|
621
|
+
{ "maxLength" => 30 },
|
622
|
+
{ "minLength" => 3 }
|
623
|
+
]
|
624
|
+
)
|
625
|
+
data_sample["contrived"] = "ab"
|
626
|
+
refute_valid
|
627
|
+
assert_includes error_messages, %{At least 3 characters are required; only 2 were supplied.}
|
628
|
+
assert_includes error_data, "ab"
|
629
|
+
end
|
630
|
+
|
631
|
+
it "includes all failing conditions for allOf as sub-errors when all_of_sub_errors is true" do
|
632
|
+
JsonSchema.configure do |c|
|
633
|
+
c.all_of_sub_errors = true
|
634
|
+
end
|
635
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
636
|
+
"allOf" => [
|
637
|
+
{ "minLength" => 5 },
|
638
|
+
{ "minLength" => 3 }
|
639
|
+
]
|
640
|
+
)
|
641
|
+
data_sample["contrived"] = "ab"
|
642
|
+
refute_valid
|
643
|
+
assert_includes error_messages, %{Not all subschemas of "allOf" matched.}
|
644
|
+
assert_includes error_types, :all_of_failed
|
645
|
+
all_of_error = @validator.errors.find { |error| error.type == :all_of_failed }
|
646
|
+
sub_error_messages = all_of_error.sub_errors.map { |errors| errors.map(&:message) }
|
647
|
+
sub_error_types = all_of_error.sub_errors.map { |errors| errors.map(&:type) }
|
648
|
+
assert_includes sub_error_messages, [%{At least 3 characters are required; only 2 were supplied.}]
|
649
|
+
assert_includes sub_error_messages, [%{At least 5 characters are required; only 2 were supplied.}]
|
650
|
+
assert_equal sub_error_types, [[:min_length_failed], [:min_length_failed]]
|
651
|
+
assert_includes error_data, "ab"
|
652
|
+
end
|
653
|
+
|
654
|
+
it "validates anyOf" do
|
655
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
656
|
+
"anyOf" => [
|
657
|
+
{ "minLength" => 5 },
|
658
|
+
{ "minLength" => 3 }
|
659
|
+
]
|
660
|
+
)
|
661
|
+
data_sample["contrived"] = "ab"
|
662
|
+
refute_valid
|
663
|
+
assert_includes error_messages, %{No subschema in "anyOf" matched.}
|
664
|
+
assert_includes error_types, :any_of_failed
|
665
|
+
any_of_error = @validator.errors.find { |error| error.type == :any_of_failed }
|
666
|
+
sub_error_messages = any_of_error.sub_errors.map { |errors| errors.map(&:message) }
|
667
|
+
sub_error_types = any_of_error.sub_errors.map { |errors| errors.map(&:type) }
|
668
|
+
assert_includes sub_error_messages, [%{At least 5 characters are required; only 2 were supplied.}]
|
669
|
+
assert_includes sub_error_messages, [%{At least 3 characters are required; only 2 were supplied.}]
|
670
|
+
assert_equal sub_error_types, [[:min_length_failed], [:min_length_failed]]
|
671
|
+
assert_includes error_data, "ab"
|
672
|
+
end
|
673
|
+
|
674
|
+
it "validates oneOf" do
|
675
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
676
|
+
"oneOf" => [
|
677
|
+
{ "pattern" => "^(foo|aaa)$" },
|
678
|
+
{ "pattern" => "^(foo|zzz)$" },
|
679
|
+
{ "pattern" => "^(hell|no)$" }
|
680
|
+
]
|
681
|
+
)
|
682
|
+
data_sample["contrived"] = "foo"
|
683
|
+
refute_valid
|
684
|
+
assert_includes error_messages, %{More than one subschema in "oneOf" matched.}
|
685
|
+
assert_includes error_types, :one_of_failed
|
686
|
+
one_of_error = @validator.errors.find { |error| error.type == :one_of_failed }
|
687
|
+
sub_error_messages = one_of_error.sub_errors.map { |errors| errors.map(&:message) }
|
688
|
+
sub_error_types = one_of_error.sub_errors.map { |errors| errors.map(&:type) }
|
689
|
+
assert_equal sub_error_messages, [[], [], [%{foo does not match /^(hell|no)$/.}]]
|
690
|
+
assert_equal sub_error_types, [[], [], [:pattern_failed]]
|
691
|
+
assert_includes error_data, "foo"
|
692
|
+
end
|
693
|
+
|
694
|
+
it "validates not" do
|
695
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
696
|
+
"not" => { "pattern" => "^$" }
|
697
|
+
)
|
698
|
+
data_sample["contrived"] = ""
|
699
|
+
refute_valid
|
700
|
+
assert_includes error_messages, %{Matched "not" subschema.}
|
701
|
+
assert_includes error_types, :not_failed
|
702
|
+
assert_includes error_data, ""
|
703
|
+
end
|
704
|
+
|
705
|
+
it "validates date format successfully" do
|
706
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
707
|
+
"format" => "date"
|
708
|
+
)
|
709
|
+
data_sample["owner"] = "2014-05-13"
|
710
|
+
assert_valid
|
711
|
+
end
|
712
|
+
|
713
|
+
it "validates date format unsuccessfully" do
|
714
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
715
|
+
"format" => "date"
|
716
|
+
)
|
717
|
+
data_sample["owner"] = "13/05/2014"
|
718
|
+
refute_valid
|
719
|
+
end
|
720
|
+
|
721
|
+
it "validates date-time format successfully" do
|
722
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
723
|
+
"format" => "date-time"
|
724
|
+
)
|
725
|
+
data_sample["owner"] = "2014-05-13T08:42:40Z"
|
726
|
+
assert_valid
|
727
|
+
end
|
728
|
+
|
729
|
+
it "validates date-time format with time zone successfully" do
|
730
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
731
|
+
"format" => "date-time"
|
732
|
+
)
|
733
|
+
data_sample["owner"] = "2014-05-13T08:42:40-00:00"
|
734
|
+
assert_valid
|
735
|
+
end
|
736
|
+
|
737
|
+
it "validates date-time format with time fraction successfully" do
|
738
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
739
|
+
"format" => "date-time"
|
740
|
+
)
|
741
|
+
data_sample["owner"] = "2014-05-13T08:42:40.444Z"
|
742
|
+
assert_valid
|
743
|
+
end
|
744
|
+
|
745
|
+
it "validates date-time format unsuccessfully" do
|
746
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
747
|
+
"format" => "date-time"
|
748
|
+
)
|
749
|
+
data_sample["owner"] = "2014-05-13T08:42:40"
|
750
|
+
refute_valid
|
751
|
+
assert_includes error_messages, %{2014-05-13T08:42:40 is not a valid date-time.}
|
752
|
+
assert_includes error_types, :invalid_format
|
753
|
+
assert_includes error_data, "2014-05-13T08:42:40"
|
754
|
+
end
|
755
|
+
|
756
|
+
it "validates email format successfully" do
|
757
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
758
|
+
"format" => "email"
|
759
|
+
)
|
760
|
+
data_sample["owner"] = "dwarf@example.com"
|
761
|
+
assert_valid
|
762
|
+
end
|
763
|
+
|
764
|
+
it "validates email format with long TLDs successfully" do
|
765
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
766
|
+
"format" => "email"
|
767
|
+
)
|
768
|
+
data_sample["owner"] = "dwarf@example.technology"
|
769
|
+
assert_valid
|
770
|
+
end
|
771
|
+
|
772
|
+
it "validates email format unsuccessfully" do
|
773
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
774
|
+
"format" => "email"
|
775
|
+
)
|
776
|
+
data_sample["owner"] = "@example.com"
|
777
|
+
refute_valid
|
778
|
+
assert_includes error_messages, %{@example.com is not a valid email.}
|
779
|
+
assert_includes error_types, :invalid_format
|
780
|
+
end
|
781
|
+
|
782
|
+
it "validates hostname format successfully" do
|
783
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
784
|
+
"format" => "hostname"
|
785
|
+
)
|
786
|
+
data_sample["owner"] = "example.com"
|
787
|
+
assert_valid
|
788
|
+
end
|
789
|
+
|
790
|
+
it "validates hostname format unsuccessfully" do
|
791
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
792
|
+
"format" => "hostname"
|
793
|
+
)
|
794
|
+
data_sample["owner"] = "@example.com"
|
795
|
+
refute_valid
|
796
|
+
assert_includes error_messages, %{@example.com is not a valid hostname.}
|
797
|
+
assert_includes error_types, :invalid_format
|
798
|
+
end
|
799
|
+
|
800
|
+
it "validates ipv4 format successfully" do
|
801
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
802
|
+
"format" => "ipv4"
|
803
|
+
)
|
804
|
+
data_sample["owner"] = "1.2.3.4"
|
805
|
+
assert_valid
|
806
|
+
end
|
807
|
+
|
808
|
+
it "validates ipv4 format unsuccessfully" do
|
809
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
810
|
+
"format" => "ipv4"
|
811
|
+
)
|
812
|
+
data_sample["owner"] = "1.2.3.4.5"
|
813
|
+
refute_valid
|
814
|
+
assert_includes error_messages, %{1.2.3.4.5 is not a valid ipv4.}
|
815
|
+
assert_includes error_types, :invalid_format
|
816
|
+
end
|
817
|
+
|
818
|
+
it "validates ipv6 format successfully" do
|
819
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
820
|
+
"format" => "ipv6"
|
821
|
+
)
|
822
|
+
data_sample["owner"] = "1::3:4:5:6:7:8"
|
823
|
+
assert_valid
|
824
|
+
end
|
825
|
+
|
826
|
+
it "validates ipv6 format unsuccessfully" do
|
827
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
828
|
+
"format" => "ipv6"
|
829
|
+
)
|
830
|
+
data_sample["owner"] = "1::3:4:5:6:7:8:9"
|
831
|
+
refute_valid
|
832
|
+
assert_includes error_messages, %{1::3:4:5:6:7:8:9 is not a valid ipv6.}
|
833
|
+
assert_includes error_types, :invalid_format
|
834
|
+
end
|
835
|
+
|
836
|
+
it "validates regex format successfully" do
|
837
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
838
|
+
"format" => "regex"
|
839
|
+
)
|
840
|
+
data_sample["owner"] = "^owner@heroku\.com$"
|
841
|
+
assert_valid
|
842
|
+
end
|
843
|
+
|
844
|
+
it "validates regex format successfully" do
|
845
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
846
|
+
"format" => "regex"
|
847
|
+
)
|
848
|
+
data_sample["owner"] = "^owner($"
|
849
|
+
refute_valid
|
850
|
+
assert_includes error_messages, %{^owner($ is not a valid regex.}
|
851
|
+
assert_includes error_types, :invalid_format
|
852
|
+
end
|
853
|
+
|
854
|
+
it "validates absolute uri format successfully" do
|
855
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
856
|
+
"format" => "uri"
|
857
|
+
)
|
858
|
+
data_sample["owner"] = "https://example.com"
|
859
|
+
assert_valid
|
860
|
+
end
|
861
|
+
|
862
|
+
it "validates relative uri format successfully" do
|
863
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
864
|
+
"format" => "uri"
|
865
|
+
)
|
866
|
+
data_sample["owner"] = "schemata/app"
|
867
|
+
assert_valid
|
868
|
+
end
|
869
|
+
|
870
|
+
it "validates uri format unsuccessfully" do
|
871
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
872
|
+
"format" => "uri"
|
873
|
+
)
|
874
|
+
data_sample["owner"] = "http://example.com[]"
|
875
|
+
refute_valid
|
876
|
+
assert_includes error_messages, %{http://example.com[] is not a valid uri.}
|
877
|
+
assert_includes error_types, :invalid_format
|
878
|
+
end
|
879
|
+
|
880
|
+
it "validates absolute uri-reference format successfully" do
|
881
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
882
|
+
"format" => "uri-reference"
|
883
|
+
)
|
884
|
+
data_sample["owner"] = "https://example.com"
|
885
|
+
assert_valid
|
886
|
+
end
|
887
|
+
|
888
|
+
it "validates relative uri format successfully" do
|
889
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
890
|
+
"format" => "uri"
|
891
|
+
)
|
892
|
+
data_sample["owner"] = "#hello"
|
893
|
+
assert_valid
|
894
|
+
end
|
895
|
+
|
896
|
+
it "validates uri format unsuccessfully" do
|
897
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
898
|
+
"format" => "uri-reference"
|
899
|
+
)
|
900
|
+
data_sample["owner"] = "http://example.com[]"
|
901
|
+
refute_valid
|
902
|
+
assert_includes error_messages, %{http://example.com[] is not a valid uri-reference.}
|
903
|
+
assert_includes error_types, :invalid_format
|
904
|
+
end
|
905
|
+
|
906
|
+
it "validates uuid format successfully" do
|
907
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
908
|
+
"format" => "uuid"
|
909
|
+
)
|
910
|
+
data_sample["owner"] = "01234567-89ab-cdef-0123-456789abcdef"
|
911
|
+
assert_valid
|
912
|
+
end
|
913
|
+
|
914
|
+
it "validates uuid format unsuccessfully" do
|
915
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
916
|
+
"format" => "uuid"
|
917
|
+
)
|
918
|
+
data_sample["owner"] = "123"
|
919
|
+
refute_valid
|
920
|
+
assert_includes error_messages, %{123 is not a valid uuid.}
|
921
|
+
assert_includes error_types, :invalid_format
|
922
|
+
end
|
923
|
+
|
924
|
+
it "validates maxLength" do
|
925
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
926
|
+
"maxLength" => 3
|
927
|
+
)
|
928
|
+
data_sample["name"] = "abcd"
|
929
|
+
refute_valid
|
930
|
+
assert_includes error_messages, %{Only 3 characters are allowed; 4 were supplied.}
|
931
|
+
assert_includes error_types, :max_length_failed
|
932
|
+
end
|
933
|
+
|
934
|
+
it "validates minLength" do
|
935
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
936
|
+
"minLength" => 3
|
937
|
+
)
|
938
|
+
data_sample["name"] = "ab"
|
939
|
+
refute_valid
|
940
|
+
assert_includes error_messages, %{At least 3 characters are required; only 2 were supplied.}
|
941
|
+
assert_includes error_types, :min_length_failed
|
942
|
+
end
|
943
|
+
|
944
|
+
it "validates pattern" do
|
945
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
946
|
+
"pattern" => "^[a-z][a-z0-9-]{3,30}$",
|
947
|
+
)
|
948
|
+
data_sample["name"] = "ab"
|
949
|
+
refute_valid
|
950
|
+
assert_includes error_messages, %{ab does not match /^[a-z][a-z0-9-]{3,30}$/.}
|
951
|
+
assert_includes error_types, :pattern_failed
|
952
|
+
assert_includes error_data, "ab"
|
953
|
+
end
|
954
|
+
|
955
|
+
it "builds appropriate JSON Pointers to bad data" do
|
956
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
957
|
+
"enum" => ["private", "public"]
|
958
|
+
)
|
959
|
+
data_sample["visibility"] = "personal"
|
960
|
+
refute_valid
|
961
|
+
assert_equal "#/visibility", @validator.errors[0].pointer
|
962
|
+
end
|
963
|
+
|
964
|
+
=begin
|
965
|
+
it "handles a validation loop" do
|
966
|
+
pointer("#/definitions/app").merge!(
|
967
|
+
"not" => { "$ref" => "#/definitions/app" }
|
968
|
+
)
|
969
|
+
data_sample["visibility"] = "personal"
|
970
|
+
refute_valid
|
971
|
+
assert_includes error_messages, %{Validation loop detected.}
|
972
|
+
end
|
973
|
+
=end
|
974
|
+
|
975
|
+
it "validates custom formats successfully" do
|
976
|
+
JsonSchema.configure do |c|
|
977
|
+
c.register_format "the-answer", ->(data) { data.to_i == 42 }
|
978
|
+
end
|
979
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
980
|
+
"format" => "the-answer"
|
981
|
+
)
|
982
|
+
data_sample["owner"] = "42"
|
983
|
+
assert_valid
|
984
|
+
end
|
985
|
+
|
986
|
+
it "validates custom formats unsuccessfully" do
|
987
|
+
JsonSchema.configure do |c|
|
988
|
+
c.register_format "the-answer", ->(data) { data.to_i == 42 }
|
989
|
+
end
|
990
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
991
|
+
"format" => "the-answer"
|
992
|
+
)
|
993
|
+
data_sample["owner"] = "43"
|
994
|
+
refute_valid
|
995
|
+
assert_includes error_messages, %{43 is not a valid the-answer.}
|
996
|
+
assert_includes error_types, :invalid_format
|
997
|
+
end
|
998
|
+
|
999
|
+
it "raises an aggregate error with validate!" do
|
1000
|
+
pointer("#/definitions/app").merge!(
|
1001
|
+
"type" => ["object"]
|
1002
|
+
)
|
1003
|
+
|
1004
|
+
schema = JsonSchema.parse!(schema_sample)
|
1005
|
+
schema.expand_references!
|
1006
|
+
schema = schema.definitions["app"]
|
1007
|
+
validator = JsonSchema::Validator.new(schema)
|
1008
|
+
|
1009
|
+
# don't bother checking the particulars of the error here because we have
|
1010
|
+
# other tests for that above
|
1011
|
+
assert_raises JsonSchema::AggregateError do
|
1012
|
+
validator.validate!(4)
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def data_sample
|
1017
|
+
@data_sample ||= DataScaffold.data_sample
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def error_messages
|
1021
|
+
@validator.errors.map(&:message)
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def error_data
|
1025
|
+
@validator.errors.map(&:data)
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
def error_types
|
1029
|
+
@validator.errors.map(&:type)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
def pointer(path)
|
1033
|
+
JsonPointer.evaluate(schema_sample, path)
|
1034
|
+
end
|
1035
|
+
|
1036
|
+
def validate_parentless_pattern
|
1037
|
+
schema = {
|
1038
|
+
"$schema" => "http://json-schema.org/draft-04/hyper-schema",
|
1039
|
+
"patternProperties" => {
|
1040
|
+
"^S_" => {
|
1041
|
+
"type" => [
|
1042
|
+
"string"
|
1043
|
+
]
|
1044
|
+
}
|
1045
|
+
}
|
1046
|
+
}
|
1047
|
+
schema = JsonSchema.parse!(schema)
|
1048
|
+
@validator = JsonSchema::Validator.new(schema)
|
1049
|
+
@validator.validate(data_sample)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
def schema_sample
|
1053
|
+
@schema_sample ||= DataScaffold.schema_sample
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
def validator
|
1057
|
+
@schema = JsonSchema.parse!(schema_sample)
|
1058
|
+
@schema.expand_references!
|
1059
|
+
@schema = @schema.definitions["app"]
|
1060
|
+
JsonSchema::Validator.new(@schema)
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
# assert_valid asserts that both the "fail fast" and the "full error messages"
|
1064
|
+
# code paths consider the data sample valid for the set schema.
|
1065
|
+
def assert_valid
|
1066
|
+
@validator = validator
|
1067
|
+
assert @validator.validate(data_sample, fail_fast: true)
|
1068
|
+
assert @validator.validate(data_sample, fail_fast: false)
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
# refute_valid asserts that both the "fail fast" and the "full error messages"
|
1072
|
+
# code paths consider the data sample erroneous for the set schema.
|
1073
|
+
def refute_valid
|
1074
|
+
@validator = validator
|
1075
|
+
refute @validator.validate(data_sample, fail_fast: true)
|
1076
|
+
refute @validator.validate(data_sample, fail_fast: false)
|
1077
|
+
end
|
1078
|
+
end
|