avro 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,9 +5,9 @@
5
5
  # to you under the Apache License, Version 2.0 (the
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
- #
8
+ #
9
9
  # http://www.apache.org/licenses/LICENSE-2.0
10
- #
10
+ #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
13
13
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -139,7 +139,7 @@ EOS
139
139
 
140
140
  }
141
141
  EOS
142
- ExampleProtocol.new(<<-EOS, true)
142
+ ExampleProtocol.new(<<-EOS, true),
143
143
  {"namespace": "org.apache.avro.test",
144
144
  "protocol": "BulkData",
145
145
 
@@ -160,6 +160,29 @@ EOS
160
160
  }
161
161
 
162
162
  }
163
+ EOS
164
+ ExampleProtocol.new(<<-EOS, true),
165
+ {
166
+ "namespace": "com.acme",
167
+ "protocol": "HelloWorld",
168
+ "doc": "protocol_documentation",
169
+
170
+ "types": [
171
+ {"name": "Greeting", "type": "record", "fields": [
172
+ {"name": "message", "type": "string"}]},
173
+ {"name": "Curse", "type": "error", "fields": [
174
+ {"name": "message", "type": "string"}]}
175
+ ],
176
+
177
+ "messages": {
178
+ "hello": {
179
+ "doc": "message_documentation",
180
+ "request": [{"name": "greeting", "type": "Greeting" }],
181
+ "response": "Greeting",
182
+ "errors": ["Curse"]
183
+ }
184
+ }
185
+ }
163
186
  EOS
164
187
  ]
165
188
 
@@ -196,4 +219,14 @@ EOS
196
219
  assert_equal type.namespace, 'com.acme'
197
220
  end
198
221
  end
222
+
223
+ def test_protocol_doc_attribute
224
+ original = Protocol.parse(EXAMPLES.last.protocol_string)
225
+ assert_equal 'protocol_documentation', original.doc
226
+ end
227
+
228
+ def test_protocol_message_doc_attribute
229
+ original = Protocol.parse(EXAMPLES.last.protocol_string)
230
+ assert_equal 'message_documentation', original.messages['hello'].doc
231
+ end
199
232
  end
@@ -17,6 +17,10 @@
17
17
  require 'test_help'
18
18
 
19
19
  class TestSchema < Test::Unit::TestCase
20
+ def hash_to_schema(hash)
21
+ Avro::Schema.parse(hash.to_json)
22
+ end
23
+
20
24
  def test_default_namespace
21
25
  schema = Avro::Schema.parse <<-SCHEMA
22
26
  {"type": "record", "name": "OuterRecord", "fields": [
@@ -27,13 +31,13 @@ class TestSchema < Test::Unit::TestCase
27
31
  ]}
28
32
  SCHEMA
29
33
 
30
- assert_equal schema.name, 'OuterRecord'
31
- assert_equal schema.fullname, 'OuterRecord'
34
+ assert_equal 'OuterRecord', schema.name
35
+ assert_equal 'OuterRecord', schema.fullname
32
36
  assert_nil schema.namespace
33
37
 
34
38
  schema.fields.each do |field|
35
- assert_equal field.type.name, 'InnerRecord'
36
- assert_equal field.type.fullname, 'InnerRecord'
39
+ assert_equal 'InnerRecord', field.type.name
40
+ assert_equal 'InnerRecord', field.type.fullname
37
41
  assert_nil field.type.namespace
38
42
  end
39
43
  end
@@ -50,13 +54,13 @@ class TestSchema < Test::Unit::TestCase
50
54
  ]}
51
55
  SCHEMA
52
56
 
53
- assert_equal schema.name, 'OuterRecord'
54
- assert_equal schema.fullname, 'my.name.space.OuterRecord'
55
- assert_equal schema.namespace, 'my.name.space'
57
+ assert_equal 'OuterRecord', schema.name
58
+ assert_equal 'my.name.space.OuterRecord', schema.fullname
59
+ assert_equal 'my.name.space', schema.namespace
56
60
  schema.fields.each do |field|
57
- assert_equal field.type.name, 'InnerRecord'
58
- assert_equal field.type.fullname, 'my.name.space.InnerRecord'
59
- assert_equal field.type.namespace, 'my.name.space'
61
+ assert_equal 'InnerRecord', field.type.name
62
+ assert_equal 'my.name.space.InnerRecord', field.type.fullname
63
+ assert_equal 'my.name.space', field.type.namespace
60
64
  end
61
65
  end
62
66
 
@@ -71,13 +75,13 @@ class TestSchema < Test::Unit::TestCase
71
75
  ]}
72
76
  SCHEMA
73
77
 
74
- assert_equal schema.name, 'OuterRecord'
75
- assert_equal schema.fullname, 'my.name.space.OuterRecord'
76
- assert_equal schema.namespace, 'my.name.space'
78
+ assert_equal 'OuterRecord', schema.name
79
+ assert_equal 'my.name.space.OuterRecord', schema.fullname
80
+ assert_equal 'my.name.space', schema.namespace
77
81
  schema.fields.each do |field|
78
- assert_equal field.type.name, 'InnerEnum'
79
- assert_equal field.type.fullname, 'my.name.space.InnerEnum'
80
- assert_equal field.type.namespace, 'my.name.space'
82
+ assert_equal 'InnerEnum', field.type.name
83
+ assert_equal 'my.name.space.InnerEnum', field.type.fullname
84
+ assert_equal 'my.name.space', field.type.namespace
81
85
  end
82
86
  end
83
87
 
@@ -96,18 +100,18 @@ class TestSchema < Test::Unit::TestCase
96
100
  ]}
97
101
  SCHEMA
98
102
 
99
- assert_equal schema.name, 'OuterRecord'
100
- assert_equal schema.fullname, 'outer.OuterRecord'
101
- assert_equal schema.namespace, 'outer'
103
+ assert_equal 'OuterRecord', schema.name
104
+ assert_equal 'outer.OuterRecord', schema.fullname
105
+ assert_equal 'outer', schema.namespace
102
106
  middle = schema.fields.first.type
103
- assert_equal middle.name, 'MiddleRecord'
104
- assert_equal middle.fullname, 'middle.MiddleRecord'
105
- assert_equal middle.namespace, 'middle'
107
+ assert_equal 'MiddleRecord', middle.name
108
+ assert_equal 'middle.MiddleRecord', middle.fullname
109
+ assert_equal 'middle', middle.namespace
106
110
  inner = middle.fields.first.type
107
- assert_equal inner.name, 'InnerRecord'
108
- assert_equal inner.fullname, 'middle.InnerRecord'
109
- assert_equal inner.namespace, 'middle'
110
- assert_equal inner.fields.first.type, middle
111
+ assert_equal 'InnerRecord', inner.name
112
+ assert_equal 'middle.InnerRecord', inner.fullname
113
+ assert_equal 'middle', inner.namespace
114
+ assert_equal middle, inner.fields.first.type
111
115
  end
112
116
 
113
117
  def test_to_avro_includes_namespaces
@@ -120,7 +124,7 @@ class TestSchema < Test::Unit::TestCase
120
124
  ]}
121
125
  SCHEMA
122
126
 
123
- assert_equal schema.to_avro, {
127
+ assert_equal({
124
128
  'type' => 'record', 'name' => 'OuterRecord', 'namespace' => 'my.name.space',
125
129
  'fields' => [
126
130
  {'name' => 'definition', 'type' => {
@@ -129,6 +133,21 @@ class TestSchema < Test::Unit::TestCase
129
133
  }},
130
134
  {'name' => 'reference', 'type' => 'my.name.space.InnerFixed'}
131
135
  ]
136
+ }, schema.to_avro)
137
+ end
138
+
139
+ def test_to_avro_includes_logical_type
140
+ schema = Avro::Schema.parse <<-SCHEMA
141
+ {"type": "record", "name": "has_logical", "fields": [
142
+ {"name": "dt", "type": {"type": "int", "logicalType": "date"}}]
143
+ }
144
+ SCHEMA
145
+
146
+ assert_equal schema.to_avro, {
147
+ 'type' => 'record', 'name' => 'has_logical',
148
+ 'fields' => [
149
+ {'name' => 'dt', 'type' => {'type' => 'int', 'logicalType' => 'date'}}
150
+ ]
132
151
  }
133
152
  end
134
153
 
@@ -160,4 +179,281 @@ class TestSchema < Test::Unit::TestCase
160
179
  ]
161
180
  }
162
181
  end
182
+
183
+ def test_record_field_doc_attribute
184
+ field_schema_json = Avro::Schema.parse <<-SCHEMA
185
+ {
186
+ "type": "record",
187
+ "name": "Record",
188
+ "namespace": "my.name.space",
189
+ "fields": [
190
+ {
191
+ "name": "name",
192
+ "type": "boolean",
193
+ "doc": "documentation"
194
+ }
195
+ ]
196
+ }
197
+ SCHEMA
198
+
199
+ field_schema_hash =
200
+ {
201
+ 'type' => 'record',
202
+ 'name' => 'Record',
203
+ 'namespace' => 'my.name.space',
204
+ 'fields' => [
205
+ {
206
+ 'name' => 'name',
207
+ 'type' => 'boolean',
208
+ 'doc' => 'documentation'
209
+ }
210
+ ]
211
+ }
212
+
213
+ assert_equal field_schema_hash, field_schema_json.to_avro
214
+ end
215
+
216
+ def test_record_doc_attribute
217
+ record_schema_json = Avro::Schema.parse <<-SCHEMA
218
+ {
219
+ "type": "record",
220
+ "name": "Record",
221
+ "namespace": "my.name.space",
222
+ "doc": "documentation",
223
+ "fields": [
224
+ {
225
+ "name": "name",
226
+ "type": "boolean"
227
+ }
228
+ ]
229
+ }
230
+ SCHEMA
231
+
232
+ record_schema_hash =
233
+ {
234
+ 'type' => 'record',
235
+ 'name' => 'Record',
236
+ 'namespace' => 'my.name.space',
237
+ 'doc' => 'documentation',
238
+ 'fields' => [
239
+ {
240
+ 'name' => 'name',
241
+ 'type' => 'boolean'
242
+ }
243
+ ]
244
+ }
245
+
246
+ assert_equal record_schema_hash, record_schema_json.to_avro
247
+ end
248
+
249
+ def test_enum_doc_attribute
250
+ enum_schema_json = Avro::Schema.parse <<-SCHEMA
251
+ {
252
+ "type": "enum",
253
+ "name": "Enum",
254
+ "namespace": "my.name.space",
255
+ "doc": "documentation",
256
+ "symbols" : [
257
+ "SPADES",
258
+ "HEARTS",
259
+ "DIAMONDS",
260
+ "CLUBS"
261
+ ]
262
+ }
263
+ SCHEMA
264
+
265
+ enum_schema_hash =
266
+ {
267
+ 'type' => 'enum',
268
+ 'name' => 'Enum',
269
+ 'namespace' => 'my.name.space',
270
+ 'doc' => 'documentation',
271
+ 'symbols' => [
272
+ 'SPADES',
273
+ 'HEARTS',
274
+ 'DIAMONDS',
275
+ 'CLUBS'
276
+ ]
277
+ }
278
+ assert_equal enum_schema_hash, enum_schema_json.to_avro
279
+ end
280
+
281
+ def test_empty_record
282
+ schema = Avro::Schema.parse('{"type":"record", "name":"Empty"}')
283
+ assert_empty(schema.fields)
284
+ end
285
+
286
+ def test_empty_union
287
+ schema = Avro::Schema.parse('[]')
288
+ assert_equal(schema.to_s, '[]')
289
+ end
290
+
291
+ def test_read
292
+ schema = Avro::Schema.parse('"string"')
293
+ writer_schema = Avro::Schema.parse('"int"')
294
+ assert_false(schema.read?(writer_schema))
295
+ assert_true(schema.read?(schema))
296
+ end
297
+
298
+ def test_be_read
299
+ schema = Avro::Schema.parse('"string"')
300
+ writer_schema = Avro::Schema.parse('"int"')
301
+ assert_false(schema.be_read?(writer_schema))
302
+ assert_true(schema.be_read?(schema))
303
+ end
304
+
305
+ def test_mutual_read
306
+ schema = Avro::Schema.parse('"string"')
307
+ writer_schema = Avro::Schema.parse('"int"')
308
+ default1 = Avro::Schema.parse('{"type":"record", "name":"Default", "fields":[{"name":"i", "type":"int", "default": 1}]}')
309
+ default2 = Avro::Schema.parse('{"type":"record", "name":"Default", "fields":[{"name:":"s", "type":"string", "default": ""}]}')
310
+ assert_false(schema.mutual_read?(writer_schema))
311
+ assert_true(schema.mutual_read?(schema))
312
+ assert_true(default1.mutual_read?(default2))
313
+ end
314
+
315
+ def test_validate_defaults
316
+ exception = assert_raise(Avro::SchemaParseError) do
317
+ hash_to_schema(
318
+ type: 'record',
319
+ name: 'fruits',
320
+ fields: [
321
+ {
322
+ name: 'veggies',
323
+ type: 'string',
324
+ default: nil
325
+ }
326
+ ]
327
+ )
328
+ end
329
+ assert_equal('Error validating default for veggies: at . expected type string, got null',
330
+ exception.to_s)
331
+ end
332
+
333
+ def test_field_default_validation_disabled
334
+ Avro.disable_field_default_validation = true
335
+ assert_nothing_raised do
336
+ hash_to_schema(
337
+ type: 'record',
338
+ name: 'fruits',
339
+ fields: [
340
+ {
341
+ name: 'veggies',
342
+ type: 'string',
343
+ default: nil
344
+ }
345
+ ]
346
+ )
347
+ end
348
+ ensure
349
+ Avro.disable_field_default_validation = false
350
+ end
351
+
352
+ def test_field_default_validation_disabled_via_env
353
+ Avro.disable_field_default_validation = false
354
+ ENV['AVRO_DISABLE_FIELD_DEFAULT_VALIDATION'] = "1"
355
+
356
+ assert_nothing_raised do
357
+ hash_to_schema(
358
+ type: 'record',
359
+ name: 'fruits',
360
+ fields: [
361
+ {
362
+ name: 'veggies',
363
+ type: 'string',
364
+ default: nil
365
+ }
366
+ ]
367
+ )
368
+ end
369
+ ensure
370
+ ENV.delete('AVRO_DISABLE_FIELD_DEFAULT_VALIDATION')
371
+ Avro.disable_field_default_validation = false
372
+ end
373
+
374
+ def test_validate_record_valid_default
375
+ assert_nothing_raised(Avro::SchemaParseError) do
376
+ hash_to_schema(
377
+ type: 'record',
378
+ name: 'with_subrecord',
379
+ fields: [
380
+ {
381
+ name: 'sub',
382
+ type: {
383
+ name: 'subrecord',
384
+ type: 'record',
385
+ fields: [
386
+ { type: 'string', name: 'x' }
387
+ ]
388
+ },
389
+ default: {
390
+ x: "y"
391
+ }
392
+ }
393
+ ]
394
+ )
395
+ end
396
+ end
397
+
398
+ def test_validate_record_invalid_default
399
+ exception = assert_raise(Avro::SchemaParseError) do
400
+ hash_to_schema(
401
+ type: 'record',
402
+ name: 'with_subrecord',
403
+ fields: [
404
+ {
405
+ name: 'sub',
406
+ type: {
407
+ name: 'subrecord',
408
+ type: 'record',
409
+ fields: [
410
+ { type: 'string', name: 'x' }
411
+ ]
412
+ },
413
+ default: {
414
+ a: 1
415
+ }
416
+ }
417
+ ]
418
+ )
419
+ end
420
+ assert_equal('Error validating default for sub: at .x expected type string, got null',
421
+ exception.to_s)
422
+ end
423
+
424
+ def test_validate_union_defaults
425
+ exception = assert_raise(Avro::SchemaParseError) do
426
+ hash_to_schema(
427
+ type: 'record',
428
+ name: 'fruits',
429
+ fields: [
430
+ {
431
+ name: 'veggies',
432
+ type: %w(string null),
433
+ default: 5
434
+ }
435
+ ]
436
+ )
437
+ end
438
+ assert_equal('Error validating default for veggies: at . expected type string, got int with value 5',
439
+ exception.to_s)
440
+ end
441
+
442
+ def test_validate_union_default_first_type
443
+ exception = assert_raise(Avro::SchemaParseError) do
444
+ hash_to_schema(
445
+ type: 'record',
446
+ name: 'fruits',
447
+ fields: [
448
+ {
449
+ name: 'veggies',
450
+ type: %w(null string),
451
+ default: 'apple'
452
+ }
453
+ ]
454
+ )
455
+ end
456
+ assert_equal('Error validating default for veggies: at . expected type null, got string with value "apple"',
457
+ exception.to_s)
458
+ end
163
459
  end