api-validator 0.0.1
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/.gitignore +17 -0
- data/Gemfile +6 -0
- data/LICENSE +27 -0
- data/README.md +80 -0
- data/Rakefile +8 -0
- data/api-validator.gemspec +27 -0
- data/lib/api-validator.rb +19 -0
- data/lib/api-validator/assertion.rb +32 -0
- data/lib/api-validator/base.rb +47 -0
- data/lib/api-validator/header.rb +77 -0
- data/lib/api-validator/json.rb +56 -0
- data/lib/api-validator/json_schema.rb +228 -0
- data/lib/api-validator/json_schemas.rb +20 -0
- data/lib/api-validator/mixins.rb +7 -0
- data/lib/api-validator/mixins/deep_merge.rb +28 -0
- data/lib/api-validator/response_expectation.rb +90 -0
- data/lib/api-validator/response_expectation/results.rb +62 -0
- data/lib/api-validator/spec.rb +153 -0
- data/lib/api-validator/spec/results.rb +28 -0
- data/lib/api-validator/status.rb +37 -0
- data/lib/api-validator/version.rb +3 -0
- data/spec/header_validator_spec.rb +108 -0
- data/spec/json_schema_validator_spec.rb +463 -0
- data/spec/json_validator_spec.rb +174 -0
- data/spec/response_expectation_results_spec.rb +105 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/spec_results_spec.rb +52 -0
- data/spec/spec_spec.rb +196 -0
- data/spec/status_validator_spec.rb +46 -0
- data/spec/support/hash_wrapper.rb +50 -0
- data/spec/support/shared_examples/shared_example_declaration.rb +8 -0
- data/spec/support/shared_examples/shared_example_lookup.rb +5 -0
- data/spec/support/shared_examples/validation_declaration.rb +65 -0
- data/spec/support/validator_shared_examples.rb +21 -0
- metadata +188 -0
@@ -0,0 +1,463 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
require 'support/validator_shared_examples'
|
5
|
+
|
6
|
+
describe ApiValidator::JsonSchema do
|
7
|
+
let(:env) { HashWrapper.new(:status => 200, :response_headers => {}, :body => '') }
|
8
|
+
let(:response) { Faraday::Response.new(env) }
|
9
|
+
let(:expectation_key) { :response_body }
|
10
|
+
let(:instance) { described_class.new(:water) }
|
11
|
+
let(:res) { instance.validate(response) }
|
12
|
+
|
13
|
+
let(:water_schema) do
|
14
|
+
{
|
15
|
+
"title" => "Test Object",
|
16
|
+
"type" => "object",
|
17
|
+
"additionalProperties" => false,
|
18
|
+
"properties" => {
|
19
|
+
"water" => {
|
20
|
+
"description" => "a wet substance",
|
21
|
+
"type" => "object",
|
22
|
+
"required" => true,
|
23
|
+
"additionalProperties" => false,
|
24
|
+
|
25
|
+
"properties" => {
|
26
|
+
"depth" => {
|
27
|
+
"description" => "depth of water in meters",
|
28
|
+
"type" => "number",
|
29
|
+
"required" => true
|
30
|
+
},
|
31
|
+
"coords" => {
|
32
|
+
"description" => "location the middle of water mass",
|
33
|
+
"type" => "object",
|
34
|
+
"required" => false,
|
35
|
+
"additionalProperties" => false,
|
36
|
+
|
37
|
+
"properties" => {
|
38
|
+
"lat" => {
|
39
|
+
"description" => "latitude",
|
40
|
+
"type" => "string",
|
41
|
+
"required" => true
|
42
|
+
},
|
43
|
+
"lng" => {
|
44
|
+
"description" => "longitude",
|
45
|
+
"type" => "string",
|
46
|
+
"required" => true
|
47
|
+
}
|
48
|
+
}
|
49
|
+
},
|
50
|
+
"attributes" => {
|
51
|
+
"description" => "key/value pairs describing the water",
|
52
|
+
"type" => "object",
|
53
|
+
"required" => true
|
54
|
+
},
|
55
|
+
"lake" => {
|
56
|
+
"description" => "is it a lake?",
|
57
|
+
"type" => "boolean"
|
58
|
+
},
|
59
|
+
"volume" => {
|
60
|
+
"description" => "volume of water mass",
|
61
|
+
"type" => "integer"
|
62
|
+
}
|
63
|
+
}
|
64
|
+
},
|
65
|
+
"rivers" => {
|
66
|
+
"description" => "list of river uris",
|
67
|
+
"type" => "array",
|
68
|
+
"items" => {
|
69
|
+
"type" => "string",
|
70
|
+
"format" => "uri"
|
71
|
+
}
|
72
|
+
},
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
let(:lake_schema) do
|
78
|
+
{
|
79
|
+
"title" => "Lake Schema",
|
80
|
+
"type" => "object",
|
81
|
+
"additionalProperties" => true,
|
82
|
+
"properties" => {
|
83
|
+
"facts" => {
|
84
|
+
"description" => "Random facts about the lake",
|
85
|
+
"type" => "object",
|
86
|
+
"required" => true,
|
87
|
+
"additionalProperties" => false,
|
88
|
+
"properties" => {
|
89
|
+
"fresh water" => {
|
90
|
+
"description" => "Is it a fresh water lake?",
|
91
|
+
"type" => "boolean",
|
92
|
+
"required" => true
|
93
|
+
},
|
94
|
+
"sand" => {
|
95
|
+
"description" => "Does the lake have a sandy bottom?",
|
96
|
+
"type" => "boolean",
|
97
|
+
"required" => true
|
98
|
+
},
|
99
|
+
"boats" => {
|
100
|
+
"description" => "Are there boats on this lake?",
|
101
|
+
"type" => "boolean"
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#validate" do
|
110
|
+
before do
|
111
|
+
ApiValidator::JsonSchemas[:water] = water_schema
|
112
|
+
ApiValidator::JsonSchemas[:lake] = lake_schema
|
113
|
+
end
|
114
|
+
|
115
|
+
context "with root pointer" do
|
116
|
+
let(:instance) { described_class.new(:lake, "/content/lake") }
|
117
|
+
|
118
|
+
let(:expected_assertions) do
|
119
|
+
[
|
120
|
+
{ :op => "test", :path => "/content/lake/facts", :type => "object" },
|
121
|
+
{ :op => "test", :path => "/content/lake/facts/fresh water", :type => "boolean" },
|
122
|
+
{ :op => "test", :path => "/content/lake/facts/sand", :type => "boolean" },
|
123
|
+
]
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when expectation passes" do
|
127
|
+
let(:expected_failed_assertions) { [] }
|
128
|
+
let(:expected_diff) { [] }
|
129
|
+
|
130
|
+
context "without optional properties" do
|
131
|
+
before do
|
132
|
+
env.body = {
|
133
|
+
"content" => {
|
134
|
+
"lake" => {
|
135
|
+
"facts" => {
|
136
|
+
"fresh water" => true,
|
137
|
+
"sand" => true
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
it_behaves_like "a validator #validate method"
|
145
|
+
end
|
146
|
+
|
147
|
+
context "with optional properties" do
|
148
|
+
before do
|
149
|
+
env.body = {
|
150
|
+
"content" => {
|
151
|
+
"lake" => {
|
152
|
+
"facts" => {
|
153
|
+
"fresh water" => true,
|
154
|
+
"sand" => true,
|
155
|
+
"boats" => false
|
156
|
+
}
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
it_behaves_like "a validator #validate method"
|
163
|
+
end
|
164
|
+
|
165
|
+
context "with allowed extra properties" do
|
166
|
+
before do
|
167
|
+
env.body = {
|
168
|
+
"content" => {
|
169
|
+
"lake" => {
|
170
|
+
"facts" => {
|
171
|
+
"fresh water" => true,
|
172
|
+
"sand" => true,
|
173
|
+
"boats" => false,
|
174
|
+
},
|
175
|
+
"allowed extra" => "I can be here :D"
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
it_behaves_like "a validator #validate method"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "when expectation fails" do
|
186
|
+
context "when missing required properties" do
|
187
|
+
before do
|
188
|
+
env.body = {
|
189
|
+
"content" => {
|
190
|
+
"lake" => {
|
191
|
+
"facts" => {
|
192
|
+
"sand" => true,
|
193
|
+
"boats" => false
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
let(:expected_failed_assertions) do
|
201
|
+
[
|
202
|
+
{ :op => "test", :path => "/content/lake/facts/fresh water", :type => "boolean" }
|
203
|
+
]
|
204
|
+
end
|
205
|
+
|
206
|
+
let(:expected_diff) do
|
207
|
+
[
|
208
|
+
{ :op => "add", :path => "/content/lake/facts/fresh water", :value => false, :type => "boolean", :message => "expected type boolean, got null" }
|
209
|
+
]
|
210
|
+
end
|
211
|
+
|
212
|
+
it_behaves_like "a validator #validate method"
|
213
|
+
end
|
214
|
+
|
215
|
+
context "when extra properties present" do
|
216
|
+
before do
|
217
|
+
env.body = {
|
218
|
+
"content" => {
|
219
|
+
"lake" => {
|
220
|
+
"facts" => {
|
221
|
+
"fresh water" => true,
|
222
|
+
"sand" => true,
|
223
|
+
"boats" => false,
|
224
|
+
"air planes" => "lots and lots of them!"
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
let(:expected_failed_assertions) do
|
232
|
+
[]
|
233
|
+
end
|
234
|
+
|
235
|
+
let(:expected_diff) do
|
236
|
+
[
|
237
|
+
{ :op => "remove", :path => "/content/lake/facts/air planes" }
|
238
|
+
]
|
239
|
+
end
|
240
|
+
|
241
|
+
it_behaves_like "a validator #validate method"
|
242
|
+
end
|
243
|
+
|
244
|
+
context "when wrong type for property" do
|
245
|
+
before do
|
246
|
+
env.body = {
|
247
|
+
"content" => {
|
248
|
+
"lake" => {
|
249
|
+
"facts" => {
|
250
|
+
"fresh water" => "yes!",
|
251
|
+
"sand" => true,
|
252
|
+
"boats" => false,
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
let(:expected_failed_assertions) do
|
260
|
+
[
|
261
|
+
{ :op => "test", :path => "/content/lake/facts/fresh water", :type => "boolean" }
|
262
|
+
]
|
263
|
+
end
|
264
|
+
|
265
|
+
let(:expected_diff) do
|
266
|
+
[
|
267
|
+
{ :op => "replace", :path => "/content/lake/facts/fresh water", :value => true, :current_value => "yes!", :type => "boolean", :message => "expected type boolean, got string" },
|
268
|
+
]
|
269
|
+
end
|
270
|
+
|
271
|
+
it_behaves_like "a validator #validate method"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
context "when root pointer omitted" do
|
277
|
+
let(:instance) { described_class.new(:water) }
|
278
|
+
|
279
|
+
let(:expected_assertions) do
|
280
|
+
[
|
281
|
+
{ :op => "test", :path => "/water", :type => "object" },
|
282
|
+
{ :op => "test", :path => "/water/depth", :type => "number" },
|
283
|
+
{ :op => "test", :path => "/water/attributes", :type => "object"}
|
284
|
+
]
|
285
|
+
end
|
286
|
+
|
287
|
+
context "when expectation passes" do
|
288
|
+
let(:expected_failed_assertions) { [] }
|
289
|
+
let(:expected_diff) { [] }
|
290
|
+
|
291
|
+
context "without optional properties" do
|
292
|
+
before do
|
293
|
+
env.body = {
|
294
|
+
"water" => {
|
295
|
+
"depth" => 2_000_000_000,
|
296
|
+
"attributes" => {
|
297
|
+
"foo" => "bar"
|
298
|
+
}
|
299
|
+
}
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
it_behaves_like "a validator #validate method"
|
304
|
+
end
|
305
|
+
|
306
|
+
context "with optional properties" do
|
307
|
+
before do
|
308
|
+
env.body = {
|
309
|
+
"water" => {
|
310
|
+
"depth" => 2_000_000_000,
|
311
|
+
"attributes" => {
|
312
|
+
"foo" => "bar"
|
313
|
+
},
|
314
|
+
"coords" => {
|
315
|
+
"lat" => "-19.65",
|
316
|
+
"lng" => "86.86",
|
317
|
+
},
|
318
|
+
"lake" => true,
|
319
|
+
"volume" => 900_000_000_000_000_000
|
320
|
+
},
|
321
|
+
"rivers" => ["http://baron.example.org", "custom://user:pass@grape.super-baron.example.com:3042/some@path:foo&bar+=$,/?foo=bar"]
|
322
|
+
}
|
323
|
+
end
|
324
|
+
|
325
|
+
it_behaves_like "a validator #validate method"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
context "when expectation failes" do
|
330
|
+
context "when missing required properties" do
|
331
|
+
before do
|
332
|
+
env.body = {
|
333
|
+
"water" => {
|
334
|
+
"attributes" => {
|
335
|
+
"foo" => "bar"
|
336
|
+
},
|
337
|
+
"coords" => {
|
338
|
+
"lat" => "-19.65",
|
339
|
+
"lng" => "86.86",
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
end
|
344
|
+
|
345
|
+
let(:expected_failed_assertions) do
|
346
|
+
[
|
347
|
+
{ :op => "test", :path => "/water/depth", :type => "number" }
|
348
|
+
]
|
349
|
+
end
|
350
|
+
|
351
|
+
let(:expected_diff) do
|
352
|
+
[
|
353
|
+
{ :op => "add", :path => "/water/depth", :value => 0.0, :type => "number", :message => "expected type number, got null" }
|
354
|
+
]
|
355
|
+
end
|
356
|
+
|
357
|
+
it_behaves_like "a validator #validate method"
|
358
|
+
end
|
359
|
+
|
360
|
+
context "when extra properties present" do
|
361
|
+
before do
|
362
|
+
env.body = {
|
363
|
+
"water" => {
|
364
|
+
"depth" => 400_000_000,
|
365
|
+
"attributes" => {
|
366
|
+
"foo" => "bar"
|
367
|
+
},
|
368
|
+
"coords" => {
|
369
|
+
"lat" => "-19.65",
|
370
|
+
"lng" => "86.86",
|
371
|
+
"foo" => "bar"
|
372
|
+
}
|
373
|
+
},
|
374
|
+
"extra" => {
|
375
|
+
"something" => "else"
|
376
|
+
},
|
377
|
+
"fire" => "air"
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
let(:expected_failed_assertions) do
|
382
|
+
[]
|
383
|
+
end
|
384
|
+
|
385
|
+
let(:expected_diff) do
|
386
|
+
[
|
387
|
+
{ :op => "remove", :path => "/water/coords/foo" },
|
388
|
+
{ :op => "remove", :path => "/extra" },
|
389
|
+
{ :op => "remove", :path => "/fire" }
|
390
|
+
]
|
391
|
+
end
|
392
|
+
|
393
|
+
it_behaves_like "a validator #validate method"
|
394
|
+
end
|
395
|
+
|
396
|
+
context "when wrong type for property" do
|
397
|
+
before do
|
398
|
+
env.body = {
|
399
|
+
"water" => {
|
400
|
+
"depth" => "400_000_000",
|
401
|
+
"attributes" => {
|
402
|
+
"foo" => "bar"
|
403
|
+
},
|
404
|
+
"coords" => {
|
405
|
+
"lat" => -19.65,
|
406
|
+
"lng" => "86.86",
|
407
|
+
}
|
408
|
+
},
|
409
|
+
"rivers" => ["http://foo.example.com", 123, "https://bar.example.org", { "this" => "should be a string" }]
|
410
|
+
}
|
411
|
+
end
|
412
|
+
|
413
|
+
let(:expected_failed_assertions) do
|
414
|
+
[
|
415
|
+
{ :op => "test", :path => "/water/depth", :type => "number" }
|
416
|
+
]
|
417
|
+
end
|
418
|
+
|
419
|
+
let(:expected_diff) do
|
420
|
+
[
|
421
|
+
{ :op => "replace", :path => "/water/depth", :value => 400_000_000.0, :current_value => "400_000_000", :type => "number", :message => "expected type number, got string" },
|
422
|
+
{ :op => "replace", :path => "/water/coords/lat", :value => "-19.65", :current_value => -19.65, :type => "string", :message => "expected type string, got number" },
|
423
|
+
{ :op => "replace", :path => "/rivers/1", :value => "123", :current_value => 123, :type => "string", :message => "expected type string, got integer" },
|
424
|
+
{ :op => "replace", :path => "/rivers/3", :value => %({"this"=>"should be a string"}), :current_value => { "this" => "should be a string" }, :type => "string", :message => "expected type string, got object" },
|
425
|
+
]
|
426
|
+
end
|
427
|
+
|
428
|
+
it_behaves_like "a validator #validate method"
|
429
|
+
end
|
430
|
+
|
431
|
+
context "when wrong format for property" do
|
432
|
+
before do
|
433
|
+
env.body = {
|
434
|
+
"water" => {
|
435
|
+
"depth" => 400_000_000,
|
436
|
+
"attributes" => {
|
437
|
+
"foo" => "bar"
|
438
|
+
},
|
439
|
+
"coords" => {
|
440
|
+
"lat" => "-19.65",
|
441
|
+
"lng" => "86.86",
|
442
|
+
}
|
443
|
+
},
|
444
|
+
"rivers" => ["http://baron.example.org", "grape"]
|
445
|
+
}
|
446
|
+
end
|
447
|
+
|
448
|
+
let(:expected_failed_assertions) do
|
449
|
+
[]
|
450
|
+
end
|
451
|
+
|
452
|
+
let(:expected_diff) do
|
453
|
+
[
|
454
|
+
{ :op => "replace", :path => "/rivers/1", :value => "https://example.com", :current_value => "grape", :type => "string", :format => "uri", :message => "expected uri format" }
|
455
|
+
]
|
456
|
+
end
|
457
|
+
|
458
|
+
it_behaves_like "a validator #validate method"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|