avro 1.8.2 → 1.9.0

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,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