api-validator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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