avro 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,554 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'test_help'
18
+
19
+ class TestSchema < Test::Unit::TestCase
20
+ def validate!(schema, value, options=nil)
21
+ Avro::SchemaValidator.validate!(schema, value, options)
22
+ end
23
+
24
+ def validate_simple!(schema, value)
25
+ Avro::SchemaValidator.validate!(schema, value, recursive: false)
26
+ end
27
+
28
+ def hash_to_schema(hash)
29
+ Avro::Schema.parse(hash.to_json)
30
+ end
31
+
32
+ def assert_failed_validation(messages)
33
+ error = assert_raise(Avro::SchemaValidator::ValidationError) { yield }
34
+
35
+ assert_messages = [messages].flatten
36
+ result_errors = error.result.errors
37
+ assert_messages.each do |message|
38
+ assert(result_errors.include?(message), "expected '#{message}' to be in '#{result_errors}'")
39
+ end
40
+ assert_equal(assert_messages.size, result_errors.size)
41
+ end
42
+
43
+ def assert_valid_schema(schema, valid, invalid, simple = false)
44
+ valid.each do |value|
45
+ assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value) }
46
+ assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value, recursive: false) } if simple
47
+ end
48
+
49
+ invalid.each do |value|
50
+ assert_raise { Avro::SchemaValidator.validate!(schema, value) }
51
+ assert_raise { Avro::SchemaValidator.validate!(schema, value, recursive: false) } if simple
52
+ assert_nothing_raised { Avro::SchemaValidator.validate!(schema, value, recursive: false) } unless simple
53
+ end
54
+ end
55
+
56
+ def test_validate_nil
57
+ schema = hash_to_schema(type: 'null', name: 'name')
58
+
59
+ assert_nothing_raised { validate!(schema, nil) }
60
+ assert_nothing_raised { validate_simple!(schema, nil) }
61
+
62
+ assert_failed_validation('at . expected type null, got int with value 1') do
63
+ validate!(schema, 1)
64
+ end
65
+
66
+ assert_failed_validation('at . expected type null, got int with value 1') do
67
+ validate_simple!(schema, 1)
68
+ end
69
+ end
70
+
71
+ def test_validate_boolean
72
+ schema = hash_to_schema(type: 'boolean', name: 'name')
73
+
74
+ assert_nothing_raised { validate!(schema, true) }
75
+ assert_nothing_raised { validate!(schema, false) }
76
+ assert_nothing_raised { validate_simple!(schema, true) }
77
+ assert_nothing_raised { validate_simple!(schema, false) }
78
+
79
+ assert_failed_validation('at . expected type boolean, got int with value 1') do
80
+ validate!(schema, 1)
81
+ end
82
+ assert_failed_validation('at . expected type boolean, got int with value 1') do
83
+ validate_simple!(schema, 1)
84
+ end
85
+
86
+ assert_failed_validation('at . expected type boolean, got null') do
87
+ validate!(schema, nil)
88
+ end
89
+ assert_failed_validation('at . expected type boolean, got null') do
90
+ validate_simple!(schema, nil)
91
+ end
92
+ end
93
+
94
+ def test_fixed_size_string
95
+ schema = hash_to_schema(type: 'fixed', name: 'some', size: 3)
96
+
97
+ assert_nothing_raised { validate!(schema, 'baf') }
98
+ assert_nothing_raised { validate_simple!(schema, 'baf') }
99
+
100
+ assert_failed_validation('at . expected fixed with size 3, got "some" with size 4') do
101
+ validate!(schema, 'some')
102
+ end
103
+ assert_failed_validation('at . expected fixed with size 3, got "some" with size 4') do
104
+ validate_simple!(schema, 'some')
105
+ end
106
+
107
+ assert_failed_validation('at . expected fixed with size 3, got null') do
108
+ validate!(schema, nil)
109
+ end
110
+ assert_failed_validation('at . expected fixed with size 3, got null') do
111
+ validate_simple!(schema, nil)
112
+ end
113
+
114
+ assert_failed_validation("at . expected fixed with size 3, got \"a\u2014b\" with size 5") do
115
+ validate!(schema, "a\u2014b")
116
+ end
117
+ assert_failed_validation("at . expected fixed with size 3, got \"a\u2014b\" with size 5") do
118
+ validate_simple!(schema, "a\u2014b")
119
+ end
120
+ end
121
+
122
+ def test_original_validate_nil
123
+ schema = hash_to_schema(type: 'null', name: 'name')
124
+
125
+ assert_valid_schema(schema, [nil], ['something'], true)
126
+ end
127
+
128
+ def test_original_validate_boolean
129
+ schema = hash_to_schema(type: 'boolean', name: 'name')
130
+
131
+ assert_valid_schema(schema, [true, false], [nil, 1], true)
132
+ end
133
+
134
+ def test_validate_string
135
+ schema = hash_to_schema(type: 'string', name: 'name')
136
+
137
+ assert_valid_schema(schema, ['string'], [nil, 1], true)
138
+ end
139
+
140
+ def test_validate_bytes
141
+ schema = hash_to_schema(type: 'bytes', name: 'name')
142
+
143
+ assert_valid_schema(schema, ['string'], [nil, 1], true)
144
+ end
145
+
146
+ def test_validate_int
147
+ schema = hash_to_schema(type: 'int', name: 'name')
148
+
149
+ assert_valid_schema(
150
+ schema,
151
+ [Avro::Schema::INT_MIN_VALUE, Avro::Schema::INT_MAX_VALUE, 1],
152
+ [Avro::Schema::LONG_MIN_VALUE, Avro::Schema::LONG_MAX_VALUE, 'string'],
153
+ true
154
+ )
155
+ assert_failed_validation('at . out of bound value 9223372036854775807') do
156
+ validate!(schema, Avro::Schema::LONG_MAX_VALUE)
157
+ end
158
+ assert_failed_validation('at . out of bound value 9223372036854775807') do
159
+ validate_simple!(schema, Avro::Schema::LONG_MAX_VALUE)
160
+ end
161
+ end
162
+
163
+ def test_validate_long
164
+ schema = hash_to_schema(type: 'long', name: 'name')
165
+
166
+ assert_valid_schema(schema, [Avro::Schema::LONG_MIN_VALUE, Avro::Schema::LONG_MAX_VALUE, 1], [1.1, 'string'], true)
167
+ end
168
+
169
+ def test_validate_float
170
+ schema = hash_to_schema(type: 'float', name: 'name')
171
+
172
+ assert_valid_schema(schema, [1.1, 1, Avro::Schema::LONG_MAX_VALUE], ['string'], true)
173
+ end
174
+
175
+ def test_validate_double
176
+ schema = hash_to_schema(type: 'double', name: 'name')
177
+
178
+ assert_valid_schema(schema, [1.1, 1, Avro::Schema::LONG_MAX_VALUE], ['string'], true)
179
+ end
180
+
181
+ def test_validate_fixed
182
+ schema = hash_to_schema(type: 'fixed', name: 'name', size: 3)
183
+
184
+ assert_valid_schema(schema, ['abc'], ['ab', 1, 1.1, true], true)
185
+ end
186
+
187
+ def test_validate_original_num
188
+ schema = hash_to_schema(type: 'enum', name: 'name', symbols: %w(a b))
189
+
190
+ assert_valid_schema(schema, ['a', 'b'], ['c'], true)
191
+ end
192
+
193
+ def test_validate_record
194
+ schema = hash_to_schema(type: 'record', name: 'name', fields: [{ type: 'null', name: 'sub' }])
195
+
196
+ assert_valid_schema(schema, [{ 'sub' => nil }], [{ 'sub' => 1 }])
197
+ end
198
+
199
+ def test_validate_shallow_record
200
+ schema = hash_to_schema(
201
+ type: 'record', name: 'name', fields: [{ type: 'int', name: 'sub' }]
202
+ )
203
+
204
+ assert_nothing_raised { validate!(schema, 'sub' => 1) }
205
+ assert_nothing_raised { validate_simple!(schema, 'sub' => 1) }
206
+
207
+ assert_failed_validation('at .sub expected type int, got null') do
208
+ validate!(schema, {})
209
+ end
210
+ assert_nothing_raised { validate_simple!(schema, {}) }
211
+
212
+ assert_failed_validation('at . expected type record, got float with value 1.2') do
213
+ validate!(schema, 1.2)
214
+ end
215
+ assert_nothing_raised { validate_simple!(schema, 1.2) }
216
+
217
+ assert_failed_validation('at .sub expected type int, got float with value 1.2') do
218
+ validate!(schema, 'sub' => 1.2)
219
+ end
220
+ assert_nothing_raised { validate_simple!(schema, 'sub' => 1.2) }
221
+ end
222
+
223
+ def test_validate_array
224
+ schema = hash_to_schema(type: 'array',
225
+ name: 'person',
226
+ items: [{ type: 'int', name: 'height' }])
227
+
228
+ assert_nothing_raised { validate!(schema, []) }
229
+ assert_nothing_raised { validate_simple!(schema, []) }
230
+
231
+ assert_failed_validation 'at . expected type array, got null' do
232
+ validate!(schema, nil)
233
+ end
234
+ assert_nothing_raised { validate_simple!(schema, nil) }
235
+
236
+ assert_failed_validation('at .[0] expected type int, got null') do
237
+ validate!(schema, [nil])
238
+ end
239
+ assert_nothing_raised { validate_simple!(schema, [nil]) }
240
+
241
+ assert_failed_validation('at .[3] expected type int, got string with value "so wrong"') do
242
+ validate!(schema, [1, 3, 9, 'so wrong'])
243
+ end
244
+ assert_nothing_raised { validate_simple!(schema, [1, 3, 9, 'so wrong']) }
245
+ end
246
+
247
+ def test_validate_enum
248
+ schema = hash_to_schema(type: 'enum',
249
+ name: 'person',
250
+ symbols: %w(one two three))
251
+
252
+ assert_nothing_raised { validate!(schema, 'one') }
253
+ assert_nothing_raised { validate_simple!(schema, 'one') }
254
+
255
+ assert_failed_validation('at . expected enum with values ["one", "two", "three"], got string with value "five"') do
256
+ validate!(schema, 'five')
257
+ end
258
+ assert_failed_validation('at . expected enum with values ["one", "two", "three"], got string with value "five"') do
259
+ validate_simple!(schema, 'five')
260
+ end
261
+ end
262
+
263
+ def test_validate_union_on_primitive_types
264
+ schema = hash_to_schema(
265
+ name: 'should_not_matter',
266
+ type: 'record',
267
+ fields: [
268
+ { name: 'what_ever', type: %w(long string) }
269
+ ]
270
+ )
271
+
272
+ assert_failed_validation('at .what_ever expected union of [\'long\', \'string\'], got null') {
273
+ validate!(schema, 'what_ever' => nil)
274
+ }
275
+ assert_nothing_raised { validate_simple!(schema, 'what_ever' => nil) }
276
+ end
277
+
278
+ def test_validate_union_of_nil_and_record_inside_array
279
+ schema = hash_to_schema(
280
+ name: 'this does not matter',
281
+ type: 'record',
282
+ fields: [
283
+ {
284
+ name: 'person',
285
+ type: {
286
+ name: 'person_entry',
287
+ type: 'record',
288
+ fields: [
289
+ {
290
+ name: 'houses',
291
+ type: [
292
+ 'null',
293
+ {
294
+ name: 'houses_entry',
295
+ type: 'array',
296
+ items: [
297
+ {
298
+ name: 'house_entry',
299
+ type: 'record',
300
+ fields: [
301
+ { name: 'number_of_rooms', type: 'long' }
302
+ ]
303
+ }
304
+ ]
305
+ }
306
+ ],
307
+ }
308
+ ]
309
+ }
310
+ }
311
+ ]
312
+ )
313
+
314
+ assert_failed_validation('at .person expected type record, got null') {
315
+ validate!(schema, 'not at all' => nil)
316
+ }
317
+ assert_nothing_raised { validate_simple!(schema, 'person' => {}) }
318
+
319
+ assert_nothing_raised { validate!(schema, 'person' => {}) }
320
+ assert_nothing_raised { validate!(schema, 'person' => { houses: [] }) }
321
+ assert_nothing_raised { validate!(schema, 'person' => { 'houses' => [{ 'number_of_rooms' => 1 }] }) }
322
+
323
+ assert_nothing_raised { validate_simple!(schema, 'person' => {}) }
324
+ assert_nothing_raised { validate_simple!(schema, 'person' => { houses: [] }) }
325
+ assert_nothing_raised { validate_simple!(schema, 'person' => { 'houses' => [{ 'number_of_rooms' => 1 }] }) }
326
+
327
+ message = 'at .person.houses[1].number_of_rooms expected type long, got string with value "not valid at all"'
328
+ datum = {
329
+ 'person' => {
330
+ 'houses' => [
331
+ { 'number_of_rooms' => 2 },
332
+ { 'number_of_rooms' => 'not valid at all' }
333
+ ]
334
+ }
335
+ }
336
+ assert_failed_validation(message) { validate!(schema, datum) }
337
+ assert_nothing_raised { validate_simple!(schema, datum) }
338
+ end
339
+
340
+ def test_validate_map
341
+ schema = hash_to_schema(type: 'map',
342
+ name: 'numbers',
343
+ values: [
344
+ { name: 'some', type: 'int' }
345
+ ])
346
+
347
+ assert_nothing_raised { validate!(schema, 'some' => 1) }
348
+ assert_nothing_raised { validate_simple!(schema, 'some' => 1) }
349
+
350
+ assert_failed_validation('at .some expected type int, got string with value "nope"') do
351
+ validate!(schema, 'some' => 'nope')
352
+ end
353
+ assert_nothing_raised { validate_simple!(schema, 'some' => 'nope')}
354
+
355
+ assert_failed_validation("at . unexpected key type 'Symbol' in map") do
356
+ validate!(schema, some: 1)
357
+ end
358
+ assert_nothing_raised { validate_simple!(schema, some: 1) }
359
+
360
+ assert_failed_validation('at . expected type map, got null') do
361
+ validate!(schema, nil)
362
+ end
363
+ assert_nothing_raised { validate_simple!(schema, nil) }
364
+ end
365
+
366
+ def test_validate_deep_record
367
+ schema = hash_to_schema(type: 'record',
368
+ name: 'person',
369
+ fields: [
370
+ {
371
+ name: 'head',
372
+ type: {
373
+ name: 'head',
374
+ type: 'record',
375
+ fields: [
376
+ {
377
+ name: 'hair',
378
+ type: {
379
+ name: 'hair',
380
+ type: 'record',
381
+ fields: [
382
+ {
383
+ name: 'color',
384
+ type: 'string'
385
+ }
386
+ ]
387
+ }
388
+ }
389
+ ]
390
+ }
391
+ }
392
+ ])
393
+
394
+ assert_nothing_raised { validate!(schema, 'head' => { 'hair' => { 'color' => 'black' } }) }
395
+ assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => { 'color' => 'black' } }) }
396
+
397
+ assert_failed_validation('at .head.hair.color expected type string, got null') do
398
+ validate!(schema, 'head' => { 'hair' => { 'color' => nil } })
399
+ end
400
+ assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => { 'color' => nil } }) }
401
+
402
+ assert_failed_validation('at .head.hair.color expected type string, got null') do
403
+ validate!(schema, 'head' => { 'hair' => {} })
404
+ end
405
+ assert_nothing_raised { validate_simple!(schema, 'head' => { 'hair' => {} }) }
406
+
407
+ assert_failed_validation('at .head.hair expected type record, got null') do
408
+ validate!(schema, 'head' => {})
409
+ end
410
+ assert_nothing_raised { validate_simple!(schema, 'head' => {}) }
411
+
412
+ assert_failed_validation('at . expected type record, got null') do
413
+ validate!(schema, nil)
414
+ end
415
+ assert_nothing_raised { validate_simple!(schema, nil) }
416
+ end
417
+
418
+ def test_validate_deep_record_with_array
419
+ schema = hash_to_schema(type: 'record',
420
+ name: 'fruits',
421
+ fields: [
422
+ {
423
+ name: 'fruits',
424
+ type: {
425
+ name: 'fruits',
426
+ type: 'array',
427
+ items: [
428
+ {
429
+ name: 'fruit',
430
+ type: 'record',
431
+ fields: [
432
+ { name: 'name', type: 'string' },
433
+ { name: 'weight', type: 'float' }
434
+ ]
435
+ }
436
+ ]
437
+ }
438
+ }
439
+ ])
440
+ assert_nothing_raised { validate!(schema, 'fruits' => [{ 'name' => 'apple', 'weight' => 30.2 }]) }
441
+ assert_nothing_raised { validate_simple!(schema, 'fruits' => [{ 'name' => 'apple', 'weight' => 30.2 }]) }
442
+
443
+ assert_failed_validation('at .fruits[0].name expected type string, got null') do
444
+ validate!(schema, 'fruits' => [{ 'name' => nil, 'weight' => 30.2 }])
445
+ end
446
+ assert_nothing_raised { validate_simple!(schema, 'fruits' => [{ 'name' => nil, 'weight' => 30.2 }]) }
447
+
448
+ assert_failed_validation('at .fruits expected type array, got int with value 1') do
449
+ validate!(schema, 'fruits' => 1)
450
+ end
451
+ assert_nothing_raised { validate_simple!(schema, 'fruits' => 1) }
452
+ end
453
+
454
+ def test_validate_multiple_errors
455
+ schema = hash_to_schema(type: 'array',
456
+ name: 'ages',
457
+ items: [
458
+ { type: 'int', name: 'age' }
459
+ ])
460
+
461
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
462
+ validate!(schema, [nil, 'e'])
463
+ end
464
+ assert_nothing_raised { validate_simple!(schema, [nil, 'e']) }
465
+ assert_equal 2, exception.result.errors.size
466
+ assert_equal(
467
+ "at .[0] expected type int, got null\nat .[1] expected type int, got string with value \"e\"",
468
+ exception.to_s
469
+ )
470
+ end
471
+
472
+ def test_validate_extra_fields
473
+ schema = hash_to_schema(
474
+ type: 'record',
475
+ name: 'fruits',
476
+ fields: [
477
+ {
478
+ name: 'veggies',
479
+ type: 'string'
480
+ }
481
+ ]
482
+ )
483
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
484
+ validate!(schema, {'veggies' => 'tomato', 'bread' => 'rye'}, fail_on_extra_fields: true)
485
+ end
486
+ assert_equal(1, exception.result.errors.size)
487
+ assert_equal("at . extra field 'bread' - not in schema",
488
+ exception.to_s)
489
+ end
490
+
491
+ def test_validate_subrecord_extra_fields
492
+ schema = hash_to_schema(type: 'record',
493
+ name: 'top',
494
+ fields: [
495
+ {
496
+ name: 'fruit',
497
+ type: {
498
+ name: 'fruit',
499
+ type: 'record',
500
+ fields: [{ name: 'name', type: 'string' }]
501
+ }
502
+ }
503
+ ])
504
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
505
+ validate!(schema, { 'fruit' => { 'name' => 'orange', 'color' => 'orange' } }, fail_on_extra_fields: true)
506
+ end
507
+ assert_equal(1, exception.result.errors.size)
508
+ assert_equal("at .fruit extra field 'color' - not in schema", exception.to_s)
509
+ end
510
+
511
+ def test_validate_array_extra_fields
512
+ schema = hash_to_schema(type: 'array',
513
+ items: {
514
+ name: 'fruit',
515
+ type: 'record',
516
+ fields: [{ name: 'name', type: 'string' }]
517
+ })
518
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
519
+ validate!(schema, [{ 'name' => 'orange', 'color' => 'orange' }], fail_on_extra_fields: true)
520
+ end
521
+ assert_equal(1, exception.result.errors.size)
522
+ assert_equal("at .[0] extra field 'color' - not in schema", exception.to_s)
523
+ end
524
+
525
+ def test_validate_map_extra_fields
526
+ schema = hash_to_schema(type: 'map',
527
+ values: {
528
+ name: 'fruit',
529
+ type: 'record',
530
+ fields: [{ name: 'color', type: 'string' }]
531
+ })
532
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
533
+ validate!(schema, { 'apple' => { 'color' => 'green', 'extra' => 1 } }, fail_on_extra_fields: true)
534
+ end
535
+ assert_equal(1, exception.result.errors.size)
536
+ assert_equal("at .apple extra field 'extra' - not in schema", exception.to_s)
537
+ end
538
+
539
+ def test_validate_union_extra_fields
540
+ schema = hash_to_schema([
541
+ 'null',
542
+ {
543
+ type: 'record',
544
+ name: 'fruit',
545
+ fields: [{ name: 'name', type: 'string' }]
546
+ }
547
+ ])
548
+ exception = assert_raise(Avro::SchemaValidator::ValidationError) do
549
+ validate!(schema, { 'name' => 'apple', 'color' => 'green' }, fail_on_extra_fields: true)
550
+ end
551
+ assert_equal(1, exception.result.errors.size)
552
+ assert_equal("at . extra field 'color' - not in schema", exception.to_s)
553
+ end
554
+ end