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.
@@ -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