json_schema 0.0.7
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.
- data/README.md +36 -0
- data/lib/json_pointer.rb +7 -0
- data/lib/json_pointer/evaluator.rb +75 -0
- data/lib/json_reference.rb +39 -0
- data/lib/json_schema.rb +27 -0
- data/lib/json_schema/parser.rb +296 -0
- data/lib/json_schema/reference_expander.rb +156 -0
- data/lib/json_schema/schema.rb +155 -0
- data/lib/json_schema/schema_error.rb +25 -0
- data/lib/json_schema/validator.rb +405 -0
- data/test/data_scaffold.rb +238 -0
- data/test/json_pointer/evaluator_test.rb +60 -0
- data/test/json_schema/parser_test.rb +230 -0
- data/test/json_schema/reference_expander_test.rb +149 -0
- data/test/json_schema/validator_test.rb +606 -0
- data/test/json_schema_test.rb +67 -0
- data/test/test_helper.rb +4 -0
- metadata +64 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "json_schema"
|
4
|
+
|
5
|
+
describe JsonSchema::ReferenceExpander do
|
6
|
+
it "expands references" do
|
7
|
+
assert expand
|
8
|
+
|
9
|
+
# this was always a fully-defined property
|
10
|
+
referenced = @schema.definitions["app"]
|
11
|
+
# this used to be a $ref
|
12
|
+
reference = @schema.properties["app"]
|
13
|
+
|
14
|
+
assert_nil reference.reference
|
15
|
+
assert_equal referenced.description, reference.description
|
16
|
+
assert_equal referenced.id, reference.id
|
17
|
+
assert_equal referenced.type, reference.type
|
18
|
+
assert_equal referenced.uri, reference.uri
|
19
|
+
end
|
20
|
+
|
21
|
+
it "will expand anyOf" do
|
22
|
+
assert expand
|
23
|
+
schema = @schema.properties["app"].definitions["contrived_plus"]
|
24
|
+
assert_equal 3, schema.any_of[0].min_length
|
25
|
+
assert_equal 5, schema.any_of[1].min_length
|
26
|
+
end
|
27
|
+
|
28
|
+
it "will expand allOf" do
|
29
|
+
assert expand
|
30
|
+
schema = @schema.properties["app"].definitions["contrived_plus"]
|
31
|
+
assert_equal 30, schema.all_of[0].max_length
|
32
|
+
assert_equal 3, schema.all_of[1].min_length
|
33
|
+
end
|
34
|
+
|
35
|
+
it "will expand dependencies" do
|
36
|
+
assert expand
|
37
|
+
schema = @schema.properties["app"].dependencies["ssl"].properties["name"]
|
38
|
+
assert_equal ["string"], schema.type
|
39
|
+
end
|
40
|
+
|
41
|
+
it "will expand items list schema" do
|
42
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
43
|
+
"items" => {
|
44
|
+
"$ref" => "#/definitions/app/definitions/name"
|
45
|
+
}
|
46
|
+
)
|
47
|
+
assert expand
|
48
|
+
schema = @schema.properties["app"].properties["flags"].items
|
49
|
+
assert_equal ["string"], schema.type
|
50
|
+
end
|
51
|
+
|
52
|
+
it "will expand items tuple schema" do
|
53
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
54
|
+
"items" => [
|
55
|
+
{ "$ref" => "#/definitions/app/definitions/name" },
|
56
|
+
{ "$ref" => "#/definitions/app/definitions/owner" }
|
57
|
+
]
|
58
|
+
)
|
59
|
+
assert expand
|
60
|
+
schema0 = @schema.properties["app"].properties["flags"].items[0]
|
61
|
+
schema1 = @schema.properties["app"].properties["flags"].items[0]
|
62
|
+
assert_equal ["string"], schema0.type
|
63
|
+
assert_equal ["string"], schema1.type
|
64
|
+
end
|
65
|
+
|
66
|
+
it "will expand oneOf" do
|
67
|
+
assert expand
|
68
|
+
schema = @schema.properties["app"].definitions["contrived_plus"]
|
69
|
+
assert_equal /^(foo|aaa)$/, schema.one_of[0].pattern
|
70
|
+
assert_equal /^(foo|zzz)$/, schema.one_of[1].pattern
|
71
|
+
end
|
72
|
+
|
73
|
+
it "will expand not" do
|
74
|
+
assert expand
|
75
|
+
schema = @schema.properties["app"].definitions["contrived_plus"]
|
76
|
+
assert_equal /^$/, schema.not.pattern
|
77
|
+
end
|
78
|
+
|
79
|
+
it "will expand patternProperties" do
|
80
|
+
assert expand
|
81
|
+
# value ([1]) of the #first tuple in hash
|
82
|
+
schema = @schema.properties["app"].definitions["roles"].
|
83
|
+
pattern_properties.first[1]
|
84
|
+
assert_equal ["string"], schema.type
|
85
|
+
end
|
86
|
+
|
87
|
+
it "will expand hyperschema link schemas" do
|
88
|
+
assert expand
|
89
|
+
schema = @schema.properties["app"].links[0].schema.properties["name"]
|
90
|
+
assert_equal ["string"], schema.type
|
91
|
+
end
|
92
|
+
|
93
|
+
it "will perform multiple passes to resolve all references" do
|
94
|
+
schema_sample["properties"] = {
|
95
|
+
"app" => {
|
96
|
+
"$ref" => "#/properties/my-app"
|
97
|
+
},
|
98
|
+
"my-app" => {
|
99
|
+
"$ref" => "#/definitions/app"
|
100
|
+
}
|
101
|
+
}
|
102
|
+
assert expand
|
103
|
+
end
|
104
|
+
|
105
|
+
it "errors on a JSON Pointer that can't be resolved" do
|
106
|
+
schema_sample["properties"]["app"] = {
|
107
|
+
"$ref" => "#/definitions/nope"
|
108
|
+
}
|
109
|
+
refute expand
|
110
|
+
assert_includes error_messages,
|
111
|
+
%{Couldn't resolve pointer "#/definitions/nope".}
|
112
|
+
end
|
113
|
+
|
114
|
+
it "errors on a schema that can't be resolved" do
|
115
|
+
schema_sample["properties"]["app"] = {
|
116
|
+
"$ref" => "/schemata/user#/definitions/name"
|
117
|
+
}
|
118
|
+
refute expand
|
119
|
+
assert_includes error_messages,
|
120
|
+
%{Couldn't resolve references (possible circular dependency): /schemata/user#/definitions/name.}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "errors on a circular reference" do
|
124
|
+
schema_sample["definitions"]["app"] = {
|
125
|
+
"$ref" => "#/properties/app"
|
126
|
+
}
|
127
|
+
refute expand
|
128
|
+
assert_includes error_messages,
|
129
|
+
%{Couldn't resolve references (possible circular dependency): #/definitions/app.}
|
130
|
+
end
|
131
|
+
|
132
|
+
def error_messages
|
133
|
+
@expander.errors.map { |e| e.message }
|
134
|
+
end
|
135
|
+
|
136
|
+
def pointer(path)
|
137
|
+
JsonPointer.evaluate(schema_sample, path)
|
138
|
+
end
|
139
|
+
|
140
|
+
def schema_sample
|
141
|
+
@schema_sample ||= DataScaffold.schema_sample
|
142
|
+
end
|
143
|
+
|
144
|
+
def expand
|
145
|
+
@schema = JsonSchema::Parser.new.parse!(schema_sample)
|
146
|
+
@expander = JsonSchema::ReferenceExpander.new
|
147
|
+
@expander.expand(@schema)
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,606 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "json_schema"
|
4
|
+
|
5
|
+
describe JsonSchema::Validator do
|
6
|
+
it "can find data valid" do
|
7
|
+
assert validate
|
8
|
+
end
|
9
|
+
|
10
|
+
it "validates enum successfully" do
|
11
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
12
|
+
"enum" => ["private", "public"]
|
13
|
+
)
|
14
|
+
data_sample["visibility"] = "public"
|
15
|
+
assert validate
|
16
|
+
end
|
17
|
+
|
18
|
+
it "validates enum unsuccessfully" do
|
19
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
20
|
+
"enum" => ["private", "public"]
|
21
|
+
)
|
22
|
+
data_sample["visibility"] = "personal"
|
23
|
+
refute validate
|
24
|
+
assert_includes error_messages,
|
25
|
+
%{Expected data to be a member of enum ["private", "public"], value was: personal.}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "validates type successfully" do
|
29
|
+
pointer("#/definitions/app").merge!(
|
30
|
+
"type" => ["object"]
|
31
|
+
)
|
32
|
+
@data_sample = { "name" => "cloudnasium" }
|
33
|
+
assert validate
|
34
|
+
end
|
35
|
+
|
36
|
+
it "validates type unsuccessfully" do
|
37
|
+
pointer("#/definitions/app").merge!(
|
38
|
+
"type" => ["object"]
|
39
|
+
)
|
40
|
+
@data_sample = 4
|
41
|
+
refute validate
|
42
|
+
assert_includes error_messages,
|
43
|
+
%{Expected data to be of type "object"; value was: 4.}
|
44
|
+
end
|
45
|
+
|
46
|
+
it "validates items with list successfully" do
|
47
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
48
|
+
"items" => {
|
49
|
+
"pattern" => "^[a-z][a-z\\-]*[a-z]$"
|
50
|
+
}
|
51
|
+
)
|
52
|
+
data_sample["flags"] = ["websockets"]
|
53
|
+
assert validate
|
54
|
+
end
|
55
|
+
|
56
|
+
it "validates items with list unsuccessfully" do
|
57
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
58
|
+
"items" => {
|
59
|
+
"pattern" => "^[a-z][a-z\\-]*[a-z]$"
|
60
|
+
}
|
61
|
+
)
|
62
|
+
data_sample["flags"] = ["1337"]
|
63
|
+
refute validate
|
64
|
+
assert_includes error_messages,
|
65
|
+
%{Expected string to match pattern "/^[a-z][a-z\\-]*[a-z]$/", value was: 1337.}
|
66
|
+
end
|
67
|
+
|
68
|
+
it "validates items with tuple successfully" do
|
69
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
70
|
+
"items" => [
|
71
|
+
{ "enum" => ["bamboo", "cedar"] },
|
72
|
+
{ "enum" => ["http", "https"] }
|
73
|
+
]
|
74
|
+
)
|
75
|
+
data_sample["flags"] = ["cedar", "https"]
|
76
|
+
assert validate
|
77
|
+
end
|
78
|
+
|
79
|
+
it "validates items with tuple successfully with additionalItems" do
|
80
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
81
|
+
"additionalItems" => true,
|
82
|
+
"items" => [
|
83
|
+
{ "enum" => ["bamboo", "cedar"] },
|
84
|
+
{ "enum" => ["http", "https"] }
|
85
|
+
]
|
86
|
+
)
|
87
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
88
|
+
assert validate
|
89
|
+
end
|
90
|
+
|
91
|
+
it "validates items with tuple unsuccessfully for not enough items" do
|
92
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
93
|
+
"items" => [
|
94
|
+
{ "enum" => ["bamboo", "cedar"] },
|
95
|
+
{ "enum" => ["http", "https"] }
|
96
|
+
]
|
97
|
+
)
|
98
|
+
data_sample["flags"] = ["cedar"]
|
99
|
+
refute validate
|
100
|
+
assert_includes error_messages,
|
101
|
+
%{Expected array to have at least 2 item(s), had 1 item(s).}
|
102
|
+
end
|
103
|
+
|
104
|
+
it "validates items with tuple unsuccessfully for too many items" do
|
105
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
106
|
+
"additionalItems" => false,
|
107
|
+
"items" => [
|
108
|
+
{ "enum" => ["bamboo", "cedar"] },
|
109
|
+
{ "enum" => ["http", "https"] }
|
110
|
+
]
|
111
|
+
)
|
112
|
+
data_sample["flags"] = ["cedar", "https", "websockets"]
|
113
|
+
refute validate
|
114
|
+
assert_includes error_messages,
|
115
|
+
%{Expected array to have no more than 2 item(s), had 3 item(s).}
|
116
|
+
end
|
117
|
+
|
118
|
+
it "validates items with tuple unsuccessfully for non-conforming items" do
|
119
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
120
|
+
"additionalItems" => false,
|
121
|
+
"items" => [
|
122
|
+
{ "enum" => ["bamboo", "cedar"] },
|
123
|
+
{ "enum" => ["http", "https"] }
|
124
|
+
]
|
125
|
+
)
|
126
|
+
data_sample["flags"] = ["cedar", "1337"]
|
127
|
+
refute validate
|
128
|
+
assert_includes error_messages,
|
129
|
+
%{Expected data to be a member of enum ["http", "https"], value was: 1337.}
|
130
|
+
end
|
131
|
+
|
132
|
+
it "validates maxItems successfully" do
|
133
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
134
|
+
"maxItems" => 10
|
135
|
+
)
|
136
|
+
data_sample["flags"] = (0...10).to_a
|
137
|
+
assert validate
|
138
|
+
end
|
139
|
+
|
140
|
+
it "validates maxItems unsuccessfully" do
|
141
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
142
|
+
"maxItems" => 10
|
143
|
+
)
|
144
|
+
data_sample["flags"] = (0...11).to_a
|
145
|
+
refute validate
|
146
|
+
assert_includes error_messages,
|
147
|
+
%{Expected array to have no more than 10 item(s), had 11 item(s).}
|
148
|
+
end
|
149
|
+
|
150
|
+
it "validates minItems successfully" do
|
151
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
152
|
+
"minItems" => 1
|
153
|
+
)
|
154
|
+
data_sample["flags"] = ["websockets"]
|
155
|
+
assert validate
|
156
|
+
end
|
157
|
+
|
158
|
+
it "validates minItems unsuccessfully" do
|
159
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
160
|
+
"minItems" => 1
|
161
|
+
)
|
162
|
+
data_sample["flags"] = []
|
163
|
+
refute validate
|
164
|
+
assert_includes error_messages,
|
165
|
+
%{Expected array to have at least 1 item(s), had 0 item(s).}
|
166
|
+
end
|
167
|
+
|
168
|
+
it "validates uniqueItems successfully" do
|
169
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
170
|
+
"uniqueItems" => true
|
171
|
+
)
|
172
|
+
data_sample["flags"] = ["websockets"]
|
173
|
+
assert validate
|
174
|
+
end
|
175
|
+
|
176
|
+
it "validates uniqueItems unsuccessfully" do
|
177
|
+
pointer("#/definitions/app/definitions/flags").merge!(
|
178
|
+
"uniqueItems" => true
|
179
|
+
)
|
180
|
+
data_sample["flags"] = ["websockets", "websockets"]
|
181
|
+
refute validate
|
182
|
+
assert_includes error_messages,
|
183
|
+
%{Expected array items to be unique, but duplicate items were found.}
|
184
|
+
end
|
185
|
+
|
186
|
+
it "validates maximum for an integer with exclusiveMaximum false" do
|
187
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
188
|
+
"exclusiveMaximum" => false,
|
189
|
+
"maximum" => 10
|
190
|
+
)
|
191
|
+
data_sample["id"] = 11
|
192
|
+
refute validate
|
193
|
+
assert_includes error_messages,
|
194
|
+
%{Expected data to be smaller than maximum 10 (exclusive: false), value was: 11.}
|
195
|
+
end
|
196
|
+
|
197
|
+
it "validates maximum for an integer with exclusiveMaximum true" do
|
198
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
199
|
+
"exclusiveMaximum" => true,
|
200
|
+
"maximum" => 10
|
201
|
+
)
|
202
|
+
data_sample["id"] = 10
|
203
|
+
refute validate
|
204
|
+
assert_includes error_messages,
|
205
|
+
%{Expected data to be smaller than maximum 10 (exclusive: true), value was: 10.}
|
206
|
+
end
|
207
|
+
|
208
|
+
it "validates maximum for a number with exclusiveMaximum false" do
|
209
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
210
|
+
"exclusiveMaximum" => false,
|
211
|
+
"maximum" => 10.0
|
212
|
+
)
|
213
|
+
data_sample["cost"] = 10.1
|
214
|
+
refute validate
|
215
|
+
assert_includes error_messages,
|
216
|
+
%{Expected data to be smaller than maximum 10.0 (exclusive: false), value was: 10.1.}
|
217
|
+
end
|
218
|
+
|
219
|
+
it "validates maximum for a number with exclusiveMaximum true" do
|
220
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
221
|
+
"exclusiveMaximum" => true,
|
222
|
+
"maximum" => 10.0
|
223
|
+
)
|
224
|
+
data_sample["cost"] = 10.0
|
225
|
+
refute validate
|
226
|
+
assert_includes error_messages,
|
227
|
+
%{Expected data to be smaller than maximum 10.0 (exclusive: true), value was: 10.0.}
|
228
|
+
end
|
229
|
+
|
230
|
+
it "validates minimum for an integer with exclusiveMaximum false" do
|
231
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
232
|
+
"exclusiveMinimum" => false,
|
233
|
+
"minimum" => 1
|
234
|
+
)
|
235
|
+
data_sample["id"] = 0
|
236
|
+
refute validate
|
237
|
+
assert_includes error_messages,
|
238
|
+
%{Expected data to be larger than minimum 1 (exclusive: false), value was: 0.}
|
239
|
+
end
|
240
|
+
|
241
|
+
it "validates minimum for an integer with exclusiveMaximum true" do
|
242
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
243
|
+
"exclusiveMinimum" => true,
|
244
|
+
"minimum" => 1
|
245
|
+
)
|
246
|
+
data_sample["id"] = 1
|
247
|
+
refute validate
|
248
|
+
assert_includes error_messages,
|
249
|
+
%{Expected data to be larger than minimum 1 (exclusive: true), value was: 1.}
|
250
|
+
end
|
251
|
+
|
252
|
+
it "validates minimum for a number with exclusiveMaximum false" do
|
253
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
254
|
+
"exclusiveMinimum" => false,
|
255
|
+
"minimum" => 0.0
|
256
|
+
)
|
257
|
+
data_sample["cost"] = -0.01
|
258
|
+
refute validate
|
259
|
+
assert_includes error_messages,
|
260
|
+
%{Expected data to be larger than minimum 0.0 (exclusive: false), value was: -0.01.}
|
261
|
+
end
|
262
|
+
|
263
|
+
it "validates minimum for a number with exclusiveMaximum true" do
|
264
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
265
|
+
"exclusiveMinimum" => true,
|
266
|
+
"minimum" => 0.0
|
267
|
+
)
|
268
|
+
data_sample["cost"] = 0.0
|
269
|
+
refute validate
|
270
|
+
assert_includes error_messages,
|
271
|
+
%{Expected data to be larger than minimum 0.0 (exclusive: true), value was: 0.0.}
|
272
|
+
end
|
273
|
+
|
274
|
+
it "validates multipleOf for an integer" do
|
275
|
+
pointer("#/definitions/app/definitions/id").merge!(
|
276
|
+
"multipleOf" => 2
|
277
|
+
)
|
278
|
+
data_sample["id"] = 1
|
279
|
+
refute validate
|
280
|
+
assert_includes error_messages,
|
281
|
+
%{Expected data to be a multiple of 2, value was: 1.}
|
282
|
+
end
|
283
|
+
|
284
|
+
it "validates multipleOf for a number" do
|
285
|
+
pointer("#/definitions/app/definitions/cost").merge!(
|
286
|
+
"multipleOf" => 0.01
|
287
|
+
)
|
288
|
+
data_sample["cost"] = 0.005
|
289
|
+
refute validate
|
290
|
+
assert_includes error_messages,
|
291
|
+
%{Expected data to be a multiple of 0.01, value was: 0.005.}
|
292
|
+
end
|
293
|
+
|
294
|
+
it "validates additionalProperties" do
|
295
|
+
pointer("#/definitions/app").merge!(
|
296
|
+
"additionalProperties" => false
|
297
|
+
)
|
298
|
+
data_sample["foo"] = "bar"
|
299
|
+
refute validate
|
300
|
+
assert_includes error_messages, %{Extra keys in object: foo.}
|
301
|
+
end
|
302
|
+
|
303
|
+
it "validates simple dependencies" do
|
304
|
+
pointer("#/definitions/app/dependencies").merge!(
|
305
|
+
"production" => "ssl"
|
306
|
+
)
|
307
|
+
data_sample["production"] = true
|
308
|
+
refute validate
|
309
|
+
assert_includes error_messages, %{Missing required keys in object: ssl.}
|
310
|
+
end
|
311
|
+
|
312
|
+
it "validates schema dependencies" do
|
313
|
+
pointer("#/definitions/app/dependencies").merge!(
|
314
|
+
"ssl" => {
|
315
|
+
"properties" => {
|
316
|
+
"cost" => {
|
317
|
+
"minimum" => 20.0,
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
)
|
322
|
+
data_sample["cost"] = 10.0
|
323
|
+
data_sample["ssl"] = true
|
324
|
+
refute validate
|
325
|
+
assert_includes error_messages, %{Expected data to be larger than minimum 20.0 (exclusive: false), value was: 10.0.}
|
326
|
+
end
|
327
|
+
|
328
|
+
it "validates maxProperties" do
|
329
|
+
pointer("#/definitions/app").merge!(
|
330
|
+
"maxProperties" => 0
|
331
|
+
)
|
332
|
+
data_sample["name"] = "cloudnasium"
|
333
|
+
refute validate
|
334
|
+
assert_includes error_messages, %{Expected object to have a maximum of 0 property/ies; it had 1.}
|
335
|
+
end
|
336
|
+
|
337
|
+
it "validates minProperties" do
|
338
|
+
pointer("#/definitions/app").merge!(
|
339
|
+
"minProperties" => 2
|
340
|
+
)
|
341
|
+
data_sample["name"] = "cloudnasium"
|
342
|
+
refute validate
|
343
|
+
assert_includes error_messages, %{Expected object to have a minimum of 2 property/ies; it had 1.}
|
344
|
+
end
|
345
|
+
|
346
|
+
it "validates patternProperties" do
|
347
|
+
pointer("#/definitions/app/definitions/config_vars").merge!(
|
348
|
+
"patternProperties" => {
|
349
|
+
"^\\w+$" => {
|
350
|
+
"type" => ["null", "string"]
|
351
|
+
}
|
352
|
+
}
|
353
|
+
)
|
354
|
+
data_sample["config_vars"] = {
|
355
|
+
"" => 123,
|
356
|
+
"KEY" => 456
|
357
|
+
}
|
358
|
+
refute validate
|
359
|
+
assert_includes error_messages,
|
360
|
+
%{Expected data to be of type "null/string"; value was: 456.}
|
361
|
+
end
|
362
|
+
|
363
|
+
it "validates required" do
|
364
|
+
pointer("#/definitions/app/dependencies").merge!(
|
365
|
+
"required" => ["name"]
|
366
|
+
)
|
367
|
+
data_sample.delete("name")
|
368
|
+
refute validate
|
369
|
+
assert_includes error_messages, %{Missing required keys in object: name.}
|
370
|
+
end
|
371
|
+
|
372
|
+
it "validates allOf" do
|
373
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
374
|
+
"allOf" => [
|
375
|
+
{ "maxLength" => 30 },
|
376
|
+
{ "minLength" => 3 }
|
377
|
+
]
|
378
|
+
)
|
379
|
+
data_sample["contrived"] = "ab"
|
380
|
+
refute validate
|
381
|
+
assert_includes error_messages,
|
382
|
+
%{Expected string to have a minimum length of 3, was 2 character(s) long.}
|
383
|
+
end
|
384
|
+
|
385
|
+
it "validates anyOf" do
|
386
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
387
|
+
"anyOf" => [
|
388
|
+
{ "minLength" => 5 },
|
389
|
+
{ "minLength" => 3 }
|
390
|
+
]
|
391
|
+
)
|
392
|
+
data_sample["contrived"] = "ab"
|
393
|
+
refute validate
|
394
|
+
assert_includes error_messages,
|
395
|
+
%{Data did not match any subschema of "anyOf" condition.}
|
396
|
+
end
|
397
|
+
|
398
|
+
it "validates oneOf" do
|
399
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
400
|
+
"oneOf" => [
|
401
|
+
{ "pattern" => "^(foo|aaa)$" },
|
402
|
+
{ "pattern" => "^(foo|zzz)$" }
|
403
|
+
]
|
404
|
+
)
|
405
|
+
data_sample["contrived"] = "foo"
|
406
|
+
refute validate
|
407
|
+
assert_includes error_messages,
|
408
|
+
%{Data did not match exactly one subschema of "oneOf" condition.}
|
409
|
+
end
|
410
|
+
|
411
|
+
it "validates not" do
|
412
|
+
pointer("#/definitions/app/definitions/contrived").merge!(
|
413
|
+
"not" => { "pattern" => "^$" }
|
414
|
+
)
|
415
|
+
data_sample["contrived"] = ""
|
416
|
+
refute validate
|
417
|
+
assert_includes error_messages,
|
418
|
+
%{Data matched subschema of "not" condition.}
|
419
|
+
end
|
420
|
+
|
421
|
+
it "validates date-time format successfully" do
|
422
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
423
|
+
"format" => "date-time"
|
424
|
+
)
|
425
|
+
data_sample["owner"] = "2014-05-13T08:42:40Z"
|
426
|
+
assert validate
|
427
|
+
end
|
428
|
+
|
429
|
+
it "validates date-time format with time zone successfully" do
|
430
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
431
|
+
"format" => "date-time"
|
432
|
+
)
|
433
|
+
data_sample["owner"] = "2014-05-13T08:42:40-00:00"
|
434
|
+
assert validate
|
435
|
+
end
|
436
|
+
|
437
|
+
it "validates date-time format unsuccessfully" do
|
438
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
439
|
+
"format" => "date-time"
|
440
|
+
)
|
441
|
+
data_sample["owner"] = "2014-05-13T08:42:40"
|
442
|
+
refute validate
|
443
|
+
assert_includes error_messages,
|
444
|
+
%{Expected data to match "date-time" format, value was: 2014-05-13T08:42:40.}
|
445
|
+
end
|
446
|
+
|
447
|
+
it "validates email format successfully" do
|
448
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
449
|
+
"format" => "email"
|
450
|
+
)
|
451
|
+
data_sample["owner"] = "dwarf@example.com"
|
452
|
+
assert validate
|
453
|
+
end
|
454
|
+
|
455
|
+
it "validates email format unsuccessfully" do
|
456
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
457
|
+
"format" => "email"
|
458
|
+
)
|
459
|
+
data_sample["owner"] = "@example.com"
|
460
|
+
refute validate
|
461
|
+
assert_includes error_messages,
|
462
|
+
%{Expected data to match "email" format, value was: @example.com.}
|
463
|
+
end
|
464
|
+
|
465
|
+
it "validates hostname format successfully" do
|
466
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
467
|
+
"format" => "hostname"
|
468
|
+
)
|
469
|
+
data_sample["owner"] = "example.com"
|
470
|
+
assert validate
|
471
|
+
end
|
472
|
+
|
473
|
+
it "validates hostname format unsuccessfully" do
|
474
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
475
|
+
"format" => "hostname"
|
476
|
+
)
|
477
|
+
data_sample["owner"] = "@example.com"
|
478
|
+
refute validate
|
479
|
+
assert_includes error_messages,
|
480
|
+
%{Expected data to match "hostname" format, value was: @example.com.}
|
481
|
+
end
|
482
|
+
|
483
|
+
it "validates ipv4 format successfully" do
|
484
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
485
|
+
"format" => "ipv4"
|
486
|
+
)
|
487
|
+
data_sample["owner"] = "1.2.3.4"
|
488
|
+
assert validate
|
489
|
+
end
|
490
|
+
|
491
|
+
it "validates ipv4 format unsuccessfully" do
|
492
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
493
|
+
"format" => "ipv4"
|
494
|
+
)
|
495
|
+
data_sample["owner"] = "1.2.3.4.5"
|
496
|
+
refute validate
|
497
|
+
assert_includes error_messages,
|
498
|
+
%{Expected data to match "ipv4" format, value was: 1.2.3.4.5.}
|
499
|
+
end
|
500
|
+
|
501
|
+
it "validates ipv6 format successfully" do
|
502
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
503
|
+
"format" => "ipv6"
|
504
|
+
)
|
505
|
+
data_sample["owner"] = "1::3:4:5:6:7:8"
|
506
|
+
assert validate
|
507
|
+
end
|
508
|
+
|
509
|
+
it "validates ipv6 format unsuccessfully" do
|
510
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
511
|
+
"format" => "ipv6"
|
512
|
+
)
|
513
|
+
data_sample["owner"] = "1::3:4:5:6:7:8:9"
|
514
|
+
refute validate
|
515
|
+
assert_includes error_messages,
|
516
|
+
%{Expected data to match "ipv6" format, value was: 1::3:4:5:6:7:8:9.}
|
517
|
+
end
|
518
|
+
|
519
|
+
it "validates uri format successfully" do
|
520
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
521
|
+
"format" => "uri"
|
522
|
+
)
|
523
|
+
data_sample["owner"] = "https://example.com"
|
524
|
+
assert validate
|
525
|
+
end
|
526
|
+
|
527
|
+
it "validates uri format unsuccessfully" do
|
528
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
529
|
+
"format" => "uri"
|
530
|
+
)
|
531
|
+
data_sample["owner"] = "example.com"
|
532
|
+
refute validate
|
533
|
+
assert_includes error_messages,
|
534
|
+
%{Expected data to match "uri" format, value was: example.com.}
|
535
|
+
end
|
536
|
+
|
537
|
+
it "validates uuid format successfully" do
|
538
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
539
|
+
"format" => "uuid"
|
540
|
+
)
|
541
|
+
data_sample["owner"] = "01234567-89ab-cdef-0123-456789abcdef"
|
542
|
+
assert validate
|
543
|
+
end
|
544
|
+
|
545
|
+
it "validates uuid format unsuccessfully" do
|
546
|
+
pointer("#/definitions/app/definitions/owner").merge!(
|
547
|
+
"format" => "uuid"
|
548
|
+
)
|
549
|
+
data_sample["owner"] = "123"
|
550
|
+
refute validate
|
551
|
+
assert_includes error_messages,
|
552
|
+
%{Expected data to match "uuid" format, value was: 123.}
|
553
|
+
end
|
554
|
+
|
555
|
+
it "validates maxLength" do
|
556
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
557
|
+
"maxLength" => 3
|
558
|
+
)
|
559
|
+
data_sample["name"] = "abcd"
|
560
|
+
refute validate
|
561
|
+
assert_includes error_messages,
|
562
|
+
%{Expected string to have a maximum length of 3, was 4 character(s) long.}
|
563
|
+
end
|
564
|
+
|
565
|
+
it "validates minLength" do
|
566
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
567
|
+
"minLength" => 3
|
568
|
+
)
|
569
|
+
data_sample["name"] = "ab"
|
570
|
+
refute validate
|
571
|
+
assert_includes error_messages,
|
572
|
+
%{Expected string to have a minimum length of 3, was 2 character(s) long.}
|
573
|
+
end
|
574
|
+
|
575
|
+
it "validates pattern" do
|
576
|
+
pointer("#/definitions/app/definitions/name").merge!(
|
577
|
+
"pattern" => "^[a-z][a-z0-9-]{3,30}$",
|
578
|
+
)
|
579
|
+
data_sample["name"] = "ab"
|
580
|
+
refute validate
|
581
|
+
assert_includes error_messages,
|
582
|
+
%{Expected string to match pattern "/^[a-z][a-z0-9-]{3,30}$/", value was: ab.}
|
583
|
+
end
|
584
|
+
|
585
|
+
def data_sample
|
586
|
+
@data_sample ||= DataScaffold.data_sample
|
587
|
+
end
|
588
|
+
|
589
|
+
def error_messages
|
590
|
+
@validator.errors.map { |e| e.message }
|
591
|
+
end
|
592
|
+
|
593
|
+
def pointer(path)
|
594
|
+
JsonPointer.evaluate(schema_sample, path)
|
595
|
+
end
|
596
|
+
|
597
|
+
def schema_sample
|
598
|
+
@schema_sample ||= DataScaffold.schema_sample
|
599
|
+
end
|
600
|
+
|
601
|
+
def validate
|
602
|
+
@schema = JsonSchema.parse!(schema_sample).definitions["app"]
|
603
|
+
@validator = JsonSchema::Validator.new(@schema)
|
604
|
+
@validator.validate(data_sample)
|
605
|
+
end
|
606
|
+
end
|