logstash-codec-protobuf 1.2.8-jruby

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +45 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/DEVELOPER.md +2 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +202 -0
  7. data/NOTICE.TXT +4 -0
  8. data/README.md +184 -0
  9. data/docs/index.asciidoc +241 -0
  10. data/google-protobuf-lib-update.md +57 -0
  11. data/lib/logstash/codecs/protobuf.rb +735 -0
  12. data/logstash-codec-protobuf.gemspec +28 -0
  13. data/spec/codecs/pb2_spec.rb +236 -0
  14. data/spec/codecs/pb3_decode_spec.rb +445 -0
  15. data/spec/codecs/pb3_encode_spec.rb +243 -0
  16. data/spec/helpers/pb2/ColourTestcase.pb.rb +35 -0
  17. data/spec/helpers/pb2/ColourTestcase.proto +24 -0
  18. data/spec/helpers/pb2/event.pb.rb +19 -0
  19. data/spec/helpers/pb2/event.proto +12 -0
  20. data/spec/helpers/pb2/header/header.pb.rb +16 -0
  21. data/spec/helpers/pb2/header/header.proto +8 -0
  22. data/spec/helpers/pb2/human.pb.rb +26 -0
  23. data/spec/helpers/pb2/unicorn.pb.rb +19 -0
  24. data/spec/helpers/pb2/unicorn_event.pb.rb +24 -0
  25. data/spec/helpers/pb3/FantasyHorse_pb.rb +44 -0
  26. data/spec/helpers/pb3/ProbeResult_pb.rb +26 -0
  27. data/spec/helpers/pb3/dnsmessage_pb.rb +82 -0
  28. data/spec/helpers/pb3/events.proto3 +10 -0
  29. data/spec/helpers/pb3/events_pb.rb +17 -0
  30. data/spec/helpers/pb3/header/header.proto3 +7 -0
  31. data/spec/helpers/pb3/header/header_pb.rb +12 -0
  32. data/spec/helpers/pb3/integertest_pb.rb +20 -0
  33. data/spec/helpers/pb3/messageA.proto3 +12 -0
  34. data/spec/helpers/pb3/messageA_pb.rb +16 -0
  35. data/spec/helpers/pb3/messageB.proto3 +12 -0
  36. data/spec/helpers/pb3/messageB_pb.rb +16 -0
  37. data/spec/helpers/pb3/rum2_pb.rb +87 -0
  38. data/spec/helpers/pb3/rum3_pb.rb +87 -0
  39. data/spec/helpers/pb3/rum_pb.rb +87 -0
  40. data/spec/helpers/pb3/unicorn.proto3 +31 -0
  41. data/spec/helpers/pb3/unicorn_pb.rb +31 -0
  42. metadata +177 -0
@@ -0,0 +1,445 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/codecs/protobuf"
4
+ require "logstash/event"
5
+
6
+ require 'google/protobuf' # for protobuf3
7
+
8
+ # absolute path to the protobuf helpers directory
9
+ pb_include_path = File.expand_path(".") + "/spec/helpers"
10
+
11
+ require pb_include_path + '/pb3/unicorn_pb.rb'
12
+ unicorn_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Unicorn").msgclass
13
+
14
+ describe LogStash::Codecs::Protobuf do
15
+
16
+ context ".reloadable?" do
17
+ subject do
18
+ next LogStash::Codecs::Protobuf.new(
19
+ "class_name" => "Unicorn",
20
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
21
+ "protobuf_version" => 3
22
+ )
23
+ end
24
+
25
+ it "returns false" do
26
+ expect(subject.reloadable?).to be_falsey
27
+ end
28
+ end
29
+
30
+ context "config" do
31
+ context "using class_file and include_path" do
32
+ let(:plugin) {
33
+ LogStash::Codecs::Protobuf.new(
34
+ "class_name" => "Unicorn",
35
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
36
+ "class_file" => pb_include_path + '/pb3/unicorn_pb.rb',
37
+ "protobuf_version" => 3
38
+ )
39
+ }
40
+
41
+ it "should fail to register the plugin with ConfigurationError" do
42
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` and `class_file`/)
43
+ end # it
44
+ end
45
+
46
+ context "not using class_file or include_path" do
47
+ let(:plugin) {
48
+ LogStash::Codecs::Protobuf.new("class_name" => "Unicorn")
49
+ }
50
+
51
+ it "should fail to register the plugin with ConfigurationError" do
52
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` or `class_file`/)
53
+ end # it
54
+ end
55
+
56
+ RSpec::Expectations.configuration.on_potential_false_positives = :nothing
57
+
58
+ context "re-registering the plugin with a valid configuration" do
59
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
60
+ "class_name" => "A.MessageA",
61
+ "class_file" => [ pb_include_path + '/pb3/messageA_pb.rb' ],
62
+ "protobuf_version" => 3,
63
+ "protobuf_root_directory" => File.expand_path(File.dirname(__FILE__) + pb_include_path + '/pb3/'))
64
+ }
65
+
66
+ it "should not fail" do
67
+ expect {
68
+ # this triggers the `register()` method of the plugin multiple times
69
+ plugin.register
70
+ plugin.register
71
+ }.not_to raise_error(RuntimeError)
72
+ end # it
73
+ end
74
+ end # context
75
+
76
+ context "#pb3decoder_test1" do
77
+
78
+
79
+ #### Test case 1: Decode simple protobuf ####################################################################################################################
80
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new(
81
+ "class_name" => "Unicorn", "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3)
82
+ }
83
+
84
+ it "should return an event from protobuf data" do
85
+
86
+ unicorn_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Unicorn").msgclass
87
+ data = {:name => 'Pinkie', :age => 18, :is_pegasus => false, :favourite_numbers => [4711,23], :fur_colour => Colour::PINK,
88
+ :favourite_colours => [Colour::GREEN, Colour::BLUE]
89
+ }
90
+
91
+ unicorn_object = unicorn_class.new(data)
92
+ bin = unicorn_class.encode(unicorn_object)
93
+ plugin_unicorn.decode(bin) do |event|
94
+ expect(event.get("name") ).to eq(data[:name] )
95
+ expect(event.get("age") ).to eq(data[:age])
96
+ expect(event.get("fur_colour") ).to eq("PINK")
97
+ expect(event.get("favourite_numbers") ).to eq(data[:favourite_numbers])
98
+ expect(event.get("favourite_colours") ).to eq(["GREEN","BLUE"])
99
+ expect(event.get("is_pegasus") ).to eq(data[:is_pegasus] )
100
+ end
101
+ end # it
102
+ end # context
103
+
104
+ context "#pb3decoder_test2" do
105
+
106
+ #### Test case 2: decode nested protobuf ####################################################################################################################
107
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Unicorn", "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3) }
108
+
109
+ it "should return an event from protobuf data with nested classes" do
110
+ father = unicorn_class.new({:name=> "Sparkle", :age => 50, :fur_colour => 3 })
111
+ data = {:name => 'Glitter', :fur_colour => Colour::GLITTER, :father => father}
112
+
113
+ unicorn_object = unicorn_class.new(data)
114
+ bin = unicorn_class.encode(unicorn_object)
115
+ plugin_unicorn.decode(bin) do |event|
116
+ expect(event.get("name") ).to eq(data[:name] )
117
+ expect(event.get("fur_colour") ).to eq("GLITTER" )
118
+ expect(event.get("father")["name"] ).to eq(data[:father][:name] )
119
+ expect(event.get("father")["age"] ).to eq(data[:father][:age] )
120
+ expect(event.get("father")["fur_colour"] ).to eq("SILVER")
121
+
122
+ end
123
+ end # it
124
+
125
+ end # context
126
+
127
+ context "#pb3decoder_test3" do
128
+
129
+ #### Test case 3: decode ProbeResult ####################################################################################################################
130
+ let(:plugin_3) { LogStash::Codecs::Protobuf.new("class_name" => "ProbeResult", "include_path" => [pb_include_path + '/pb3/ProbeResult_pb.rb'], "protobuf_version" => 3) }
131
+
132
+ before do
133
+ plugin_3.register
134
+ end
135
+
136
+ it "should return an event from protobuf data with nested classes" do
137
+
138
+ probe_result_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("ProbeResult").msgclass
139
+ ping_result_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PingIPv4Result").msgclass
140
+
141
+ ping_result_data = {:status=> PingIPv4Result::Status::ERROR,
142
+ :latency => 50, :ip => "8.8.8.8", :probe_ip => "127.0.0.1", :geolocation => "New York City" }
143
+ ping_result_object = ping_result_class.new(ping_result_data)
144
+
145
+ probe_result_data = {:UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object}
146
+ probe_result_object = probe_result_class.new(probe_result_data)
147
+ bin = probe_result_class.encode(probe_result_object)
148
+ plugin_3.decode(bin) do |event|
149
+ expect(event.get("UUID") ).to eq(probe_result_data[:UUID] )
150
+ expect(event.get("TaskPingIPv4Result")["status"] ).to eq("ERROR")
151
+ expect(event.get("TaskPingIPv4Result")["latency"] ).to eq(ping_result_data[:latency] )
152
+ expect(event.get("TaskPingIPv4Result")["ip"] ).to eq(ping_result_data[:ip] )
153
+ expect(event.get("TaskPingIPv4Result")["probe_ip"] ).to eq(ping_result_data[:probe_ip] )
154
+ expect(event.get("TaskPingIPv4Result")["geolocation"] ).to eq(ping_result_data[:geolocation] )
155
+ end
156
+ end # it
157
+ end # context
158
+
159
+ context "#pb3decoder_test4" do
160
+
161
+ #### Test case 4: decode PBDNSMessage ####################################################################################################################
162
+ let(:plugin_4) { LogStash::Codecs::Protobuf.new("class_name" => "PBDNSMessage", "include_path" => [pb_include_path + '/pb3/dnsmessage_pb.rb'], "protobuf_version" => 3) }
163
+
164
+ before do
165
+ plugin_4.register
166
+ end
167
+
168
+ it "should return an event from protobuf data with nested classes" do
169
+
170
+
171
+ pbdns_message_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage").msgclass
172
+ dns_question_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSQuestion").msgclass
173
+ dns_response_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSResponse").msgclass
174
+ dns_rr_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSResponse.DNSRR").msgclass
175
+
176
+ dns_question_data = {:qName => "Foo", :qType => 12345, :qClass => 67890 }
177
+ dns_question_object = dns_question_class.new(dns_question_data)
178
+
179
+ dns_response_data = {:rcode => 12345, :appliedPolicy => "baz", :tags => ["a","b","c"],
180
+ :queryTimeSec => 123, :queryTimeUsec => 456,
181
+ :appliedPolicyType => PBDNSMessage::PolicyType::NSIP}
182
+
183
+ dns_rr_data = [
184
+ {:name => "abc", :type => 9000, :class => 8000, :ttl => 20, :rdata => "300"},
185
+ {:name => "def", :type => 19000, :class => 18000, :ttl => 120, :rdata => "1300"}
186
+ ]
187
+
188
+ dns_response_data[:rrs] = dns_rr_data.map { | d | d = dns_rr_class.new(d) }
189
+ dns_response_object = dns_response_class.new(dns_response_data)
190
+
191
+ pbdns_message_data = {
192
+ # :UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object
193
+ :type => PBDNSMessage::Type::DNSIncomingResponseType,
194
+ :messageId => "15",
195
+ :serverIdentity => "16",
196
+ :socketFamily => PBDNSMessage::SocketFamily::INET6,
197
+ :socketProtocol => PBDNSMessage::SocketProtocol::TCP,
198
+ :from => "17",
199
+ :to => "18",
200
+ :inBytes => 70000,
201
+ :timeSec => 80000,
202
+ :timeUsec => 90000,
203
+ :id => 20000,
204
+ :question => dns_question_object,
205
+ :response => dns_response_object,
206
+ :originalRequestorSubnet => "19",
207
+ :requestorId => "Bar",
208
+ :initialRequestId => "20",
209
+ :deviceId => "21",
210
+ }
211
+ pbdns_message_object = pbdns_message_class.new(pbdns_message_data)
212
+ bin = pbdns_message_class.encode(pbdns_message_object)
213
+ plugin_4.decode(bin) do |event|
214
+
215
+ ['messageId', 'serverIdentity','from','to','inBytes','timeUsec','timeSec','id', 'originalRequestorSubnet', 'requestorId' ,'initialRequestId','deviceIdf'].each { |n|
216
+ expect(event.get(n)).to eq(pbdns_message_data[n.to_sym] ) }
217
+
218
+ # enum test:
219
+ expect(event.get("type") ).to eq("DNSIncomingResponseType" )
220
+ expect(event.get("socketFamily") ).to eq("INET6" )
221
+ expect(event.get("socketProtocol") ).to eq("TCP" )
222
+
223
+ expect(event.get("question")["qName"] ).to eq(dns_question_data[:qName] )
224
+ expect(event.get("question")["qType"] ).to eq(dns_question_data[:qType] )
225
+ expect(event.get("question")["qClass"] ).to eq(dns_question_data[:qClass] )
226
+
227
+ ['rcode', 'appliedPolicy','tags','queryTimeSec','queryTimeUsec'].each { |n| expect(event.get('response')[n]).to eq(dns_response_data[n.to_sym] ) }
228
+ expect(event.get("response")['appliedPolicyType'] ).to eq("NSIP" )
229
+
230
+ dns_rr_data.each_with_index { | data, index |
231
+ found = event.get("response")['rrs'][index]
232
+ ['name', 'type','class','ttl','rdata'].each { |n| expect(found[n]).to eq(data[n.to_sym]) }
233
+ }
234
+
235
+ end
236
+ end # it
237
+
238
+ end # context
239
+
240
+ context "#pb3decoder_test5" do
241
+
242
+ #### Test case 5: decode test case for github issue 17 ####################################################################################################################
243
+ let(:plugin_5) { LogStash::Codecs::Protobuf.new("class_name" => "com.foo.bar.IntegerTestMessage", "include_path" => [pb_include_path + '/pb3/integertest_pb.rb'], "protobuf_version" => 3) }
244
+
245
+ before do
246
+ plugin_5.register
247
+ end
248
+
249
+ it "should return an event from protobuf data with nested classes" do
250
+ integertest_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.foo.bar.IntegerTestMessage").msgclass
251
+ integertest_object = integertest_class.new({:response_time => 500})
252
+ bin = integertest_class.encode(integertest_object)
253
+ plugin_5.decode(bin) do |event|
254
+ expect(event.get("response_time") ).to eq(500)
255
+ end
256
+ end # it
257
+
258
+
259
+ end # context
260
+
261
+ context "#pb3decoder_test6" do
262
+
263
+
264
+ let(:execution_context) { double("execution_context")}
265
+ let(:pipeline_id) {rand(36**8).to_s(36)}
266
+
267
+ # Test case 6: decode a message automatically loading the dependencies ##############################################################################
268
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
269
+ "class_name" => "A.MessageA",
270
+ "class_file" => [ 'messageA_pb.rb' ],
271
+ "protobuf_version" => 3,
272
+ "protobuf_root_directory" => pb_include_path + '/pb3/')
273
+ }
274
+
275
+ before do
276
+ allow(plugin).to receive(:execution_context).and_return(execution_context)
277
+ allow(execution_context).to receive(:pipeline_id).and_return(pipeline_id)
278
+
279
+ # this is normally done on the input plugins we "mock" it here to avoid
280
+ # instantiating a dummy input plugin. See
281
+ # https://github.com/ph/logstash/blob/37551a89b8137c1dc6fa4fbd992584c363a36065/logstash-core/lib/logstash/inputs/base.rb#L108
282
+ plugin.execution_context = execution_context
283
+ end
284
+
285
+ it "should return an event from protobuf data" do
286
+
287
+ header_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Header").msgclass
288
+ header_data = {:name => {'a' => 'b'}}
289
+ header_object = header_class.new(header_data)
290
+
291
+ message_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.MessageA").msgclass
292
+ data = {:name => "Test name", :header => header_object}
293
+
294
+ message_object = message_class.new(data)
295
+ bin = message_class.encode(message_object)
296
+
297
+ plugin.decode(bin) do |event|
298
+ expect(event.get("name") ).to eq(data[:name] )
299
+ expect(event.get("header")['name'] ).to eq(header_data[:name])
300
+ end
301
+ end # it
302
+ end # context
303
+
304
+
305
+
306
+
307
+
308
+
309
+ context "#pb3decoder_test7" do
310
+
311
+ #### Test case 6: decode test case for github issue 17 ####################################################################################################################
312
+ let(:plugin_7) { LogStash::Codecs::Protobuf.new("class_name" => "RepeatedEvents", "include_path" => [pb_include_path + '/pb3/events_pb.rb'], "protobuf_version" => 3) }
313
+ before do
314
+ plugin_7.register
315
+ end
316
+
317
+ it "should return an event from protobuf data with repeated top level objects" do
318
+ event_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("RepeatedEvent").msgclass # TODO this shouldnt be necessary because the classes are already
319
+ # specified at the end of the _pb.rb files
320
+ events_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("RepeatedEvents").msgclass
321
+ test_a = event_class.new({:id => "1", :msg => "a"})
322
+ test_b = event_class.new({:id => "2", :msg => "b"})
323
+ test_c = event_class.new({:id => "3", :msg => "c"})
324
+ event_obj = events_class.new({:repeated_events=>[test_a, test_b, test_c]})
325
+ bin = events_class.encode(event_obj)
326
+ plugin_7.decode(bin) do |event|
327
+ expect(event.get("repeated_events").size ).to eq(3)
328
+ expect(event.get("repeated_events")[0]["id"] ).to eq("1")
329
+ expect(event.get("repeated_events")[2]["id"] ).to eq("3")
330
+ expect(event.get("repeated_events")[0]["msg"] ).to eq("a")
331
+ expect(event.get("repeated_events")[2]["msg"] ).to eq("c")
332
+ end
333
+ end # it
334
+
335
+
336
+ end # context pb3decoder_test7
337
+
338
+
339
+ context "#pb3decoder_test8a" do
340
+
341
+ ########################################################################################################################
342
+ let(:plugin_8a) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
343
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
344
+ before do
345
+ plugin_8a.register
346
+ end
347
+
348
+ it "should add meta information on oneof fields" do
349
+ pegasus_data = {:wings_length => 100}
350
+ horsey = FantasyPegasus.new(pegasus_data)
351
+
352
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
353
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
354
+ tail = FantasyHorseTail.new(tail_data)
355
+
356
+ data = {:name=>"Reinhold", :pegasus => horsey, :tail => tail}
357
+ pb_obj = FantasyHorse.new(data)
358
+ bin = FantasyHorse.encode(pb_obj)
359
+ plugin_8a.decode(bin) do |event|
360
+
361
+ expect(event.get("name") ).to eq(data[:name])
362
+ expect(event.get("pegasus")["wings_length"] ).to eq(pegasus_data[:wings_length])
363
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
364
+ expect(event.get("tail")['braided']['braiding_style'] ).to eq(braid_data[:braiding_style])
365
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"] ).to eq("pegasus")
366
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"] ).to eq("braided")
367
+
368
+ end
369
+ end # it
370
+
371
+
372
+ end # context pb3decoder_test8a
373
+
374
+
375
+
376
+
377
+ context "#pb3decoder_test8b" do
378
+
379
+ ########################################################################################################################
380
+ let(:plugin_8b) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
381
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => false) }
382
+ before do
383
+ plugin_8b.register
384
+ end
385
+
386
+ it "should not add meta information on oneof fields" do
387
+ pegasus_data = {:wings_length => 100}
388
+ horsey = FantasyPegasus.new(pegasus_data)
389
+
390
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
391
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
392
+ tail = FantasyHorseTail.new(tail_data)
393
+
394
+ data = {:name=>"Winfried", :pegasus => horsey, :tail => tail}
395
+ pb_obj = FantasyHorse.new(data)
396
+ bin = FantasyHorse.encode(pb_obj)
397
+ plugin_8b.decode(bin) do |event|
398
+ expect(event.get("name") ).to eq(data[:name])
399
+ expect(event.get("pegasus")["wings_length"] ).to eq(pegasus_data[:wings_length])
400
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
401
+ expect(event.get("tail")['braided']['braiding_style'] ).to eq(braid_data[:braiding_style])
402
+ expect(event.get("@metadata")["pb_oneof"]).to be_nil
403
+
404
+ end
405
+ end # it
406
+
407
+
408
+ end # context pb3decoder_test8b
409
+
410
+
411
+ context "#pb3decoder_test8c" do # same test as 8a just with different one_of options selected
412
+
413
+ ########################################################################################################################
414
+ let(:plugin_8c) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
415
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
416
+ before do
417
+ plugin_8c.register
418
+ end
419
+
420
+ it "should add meta information on oneof fields" do
421
+ unicorn_data = {:horn_length => 30}
422
+ horsey = FantasyUnicorn.new(unicorn_data)
423
+
424
+ natural_data = {:wavyness => "B"}
425
+ tail_data = {:tail_length => 80, :natural => NaturalHorseTail.new(natural_data) }
426
+ tail = FantasyHorseTail.new(tail_data)
427
+
428
+ data = {:name=>"Hubert", :unicorn => horsey, :tail => tail}
429
+ pb_obj = FantasyHorse.new(data)
430
+ bin = FantasyHorse.encode(pb_obj)
431
+ plugin_8c.decode(bin) do |event|
432
+ expect(event.get("name") ).to eq(data[:name])
433
+ expect(event.get("unicorn")["horn_length"] ).to eq(unicorn_data[:horn_length])
434
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
435
+ expect(event.get("tail")['natural']['wavyness'] ).to eq(natural_data[:wavyness])
436
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"] ).to eq("unicorn")
437
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"] ).to eq("natural")
438
+
439
+ end
440
+ end # it
441
+
442
+
443
+ end # context pb3decoder_test8c
444
+
445
+ end # describe