logstash-codec-protobuf 1.2.8-jruby

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