protobuf-cucumber 3.10.4

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 (204) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +70 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +40 -0
  6. data/.yardopts +5 -0
  7. data/CHANGES.md +344 -0
  8. data/CONTRIBUTING.md +16 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +33 -0
  12. data/Rakefile +64 -0
  13. data/bin/protoc-gen-ruby +22 -0
  14. data/bin/rpc_server +5 -0
  15. data/install-protobuf.sh +28 -0
  16. data/lib/protobuf.rb +129 -0
  17. data/lib/protobuf/cli.rb +257 -0
  18. data/lib/protobuf/code_generator.rb +120 -0
  19. data/lib/protobuf/decoder.rb +28 -0
  20. data/lib/protobuf/deprecation.rb +117 -0
  21. data/lib/protobuf/descriptors.rb +3 -0
  22. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
  23. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
  24. data/lib/protobuf/encoder.rb +11 -0
  25. data/lib/protobuf/enum.rb +365 -0
  26. data/lib/protobuf/exceptions.rb +9 -0
  27. data/lib/protobuf/field.rb +74 -0
  28. data/lib/protobuf/field/base_field.rb +380 -0
  29. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  30. data/lib/protobuf/field/bool_field.rb +64 -0
  31. data/lib/protobuf/field/bytes_field.rb +78 -0
  32. data/lib/protobuf/field/double_field.rb +25 -0
  33. data/lib/protobuf/field/enum_field.rb +61 -0
  34. data/lib/protobuf/field/field_array.rb +104 -0
  35. data/lib/protobuf/field/field_hash.rb +122 -0
  36. data/lib/protobuf/field/fixed32_field.rb +25 -0
  37. data/lib/protobuf/field/fixed64_field.rb +28 -0
  38. data/lib/protobuf/field/float_field.rb +43 -0
  39. data/lib/protobuf/field/int32_field.rb +21 -0
  40. data/lib/protobuf/field/int64_field.rb +34 -0
  41. data/lib/protobuf/field/integer_field.rb +23 -0
  42. data/lib/protobuf/field/message_field.rb +51 -0
  43. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  44. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  45. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  46. data/lib/protobuf/field/sint32_field.rb +21 -0
  47. data/lib/protobuf/field/sint64_field.rb +21 -0
  48. data/lib/protobuf/field/string_field.rb +51 -0
  49. data/lib/protobuf/field/uint32_field.rb +21 -0
  50. data/lib/protobuf/field/uint64_field.rb +21 -0
  51. data/lib/protobuf/field/varint_field.rb +77 -0
  52. data/lib/protobuf/generators/base.rb +85 -0
  53. data/lib/protobuf/generators/enum_generator.rb +39 -0
  54. data/lib/protobuf/generators/extension_generator.rb +27 -0
  55. data/lib/protobuf/generators/field_generator.rb +193 -0
  56. data/lib/protobuf/generators/file_generator.rb +262 -0
  57. data/lib/protobuf/generators/group_generator.rb +122 -0
  58. data/lib/protobuf/generators/message_generator.rb +104 -0
  59. data/lib/protobuf/generators/option_generator.rb +17 -0
  60. data/lib/protobuf/generators/printable.rb +160 -0
  61. data/lib/protobuf/generators/service_generator.rb +50 -0
  62. data/lib/protobuf/lifecycle.rb +33 -0
  63. data/lib/protobuf/logging.rb +39 -0
  64. data/lib/protobuf/message.rb +260 -0
  65. data/lib/protobuf/message/fields.rb +233 -0
  66. data/lib/protobuf/message/serialization.rb +85 -0
  67. data/lib/protobuf/optionable.rb +70 -0
  68. data/lib/protobuf/rpc/buffer.rb +78 -0
  69. data/lib/protobuf/rpc/client.rb +140 -0
  70. data/lib/protobuf/rpc/connectors/base.rb +221 -0
  71. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  72. data/lib/protobuf/rpc/connectors/socket.rb +78 -0
  73. data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
  74. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
  75. data/lib/protobuf/rpc/env.rb +60 -0
  76. data/lib/protobuf/rpc/error.rb +28 -0
  77. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  78. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  79. data/lib/protobuf/rpc/middleware.rb +25 -0
  80. data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
  81. data/lib/protobuf/rpc/middleware/logger.rb +95 -0
  82. data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
  83. data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
  84. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  85. data/lib/protobuf/rpc/rpc.pb.rb +64 -0
  86. data/lib/protobuf/rpc/rpc_method.rb +16 -0
  87. data/lib/protobuf/rpc/server.rb +39 -0
  88. data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
  89. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  90. data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
  91. data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
  92. data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
  93. data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
  94. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  95. data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
  96. data/lib/protobuf/rpc/service.rb +172 -0
  97. data/lib/protobuf/rpc/service_directory.rb +261 -0
  98. data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
  99. data/lib/protobuf/rpc/service_filters.rb +250 -0
  100. data/lib/protobuf/rpc/stat.rb +119 -0
  101. data/lib/protobuf/socket.rb +21 -0
  102. data/lib/protobuf/tasks.rb +1 -0
  103. data/lib/protobuf/tasks/compile.rake +80 -0
  104. data/lib/protobuf/varint.rb +20 -0
  105. data/lib/protobuf/varint_pure.rb +31 -0
  106. data/lib/protobuf/version.rb +3 -0
  107. data/lib/protobuf/wire_type.rb +10 -0
  108. data/lib/protobuf/zmq.rb +21 -0
  109. data/profile.html +5103 -0
  110. data/proto/dynamic_discovery.proto +44 -0
  111. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  112. data/proto/google/protobuf/descriptor.proto +779 -0
  113. data/proto/rpc.proto +69 -0
  114. data/protobuf-cucumber.gemspec +57 -0
  115. data/spec/benchmark/tasks.rb +143 -0
  116. data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
  117. data/spec/encoding/all_types_spec.rb +103 -0
  118. data/spec/encoding/extreme_values_spec.rb +0 -0
  119. data/spec/functional/class_inheritance_spec.rb +52 -0
  120. data/spec/functional/code_generator_spec.rb +58 -0
  121. data/spec/functional/socket_server_spec.rb +59 -0
  122. data/spec/functional/zmq_server_spec.rb +105 -0
  123. data/spec/lib/protobuf/cli_spec.rb +317 -0
  124. data/spec/lib/protobuf/code_generator_spec.rb +87 -0
  125. data/spec/lib/protobuf/enum_spec.rb +307 -0
  126. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  127. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
  129. data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
  130. data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
  131. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  134. data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
  135. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  136. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  137. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  138. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  139. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  140. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  141. data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
  142. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  143. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  144. data/spec/lib/protobuf/field_spec.rb +192 -0
  145. data/spec/lib/protobuf/generators/base_spec.rb +154 -0
  146. data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
  147. data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
  148. data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
  149. data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
  150. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  151. data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
  152. data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
  153. data/spec/lib/protobuf/message_spec.rb +944 -0
  154. data/spec/lib/protobuf/optionable_spec.rb +265 -0
  155. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  156. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
  157. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  158. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
  159. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
  160. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  161. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  162. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  163. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
  164. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  165. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
  166. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  167. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  168. data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
  169. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
  170. data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
  171. data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
  172. data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
  173. data/spec/lib/protobuf/varint_spec.rb +29 -0
  174. data/spec/lib/protobuf_spec.rb +105 -0
  175. data/spec/spec_helper.rb +42 -0
  176. data/spec/support/all.rb +6 -0
  177. data/spec/support/packed_field.rb +23 -0
  178. data/spec/support/protos/all_types.data.bin +0 -0
  179. data/spec/support/protos/all_types.data.txt +119 -0
  180. data/spec/support/protos/enum.pb.rb +63 -0
  181. data/spec/support/protos/enum.proto +37 -0
  182. data/spec/support/protos/extreme_values.data.bin +0 -0
  183. data/spec/support/protos/google_unittest.bin +0 -0
  184. data/spec/support/protos/google_unittest.pb.rb +798 -0
  185. data/spec/support/protos/google_unittest.proto +884 -0
  186. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  187. data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
  188. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  189. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  190. data/spec/support/protos/google_unittest_import.proto +73 -0
  191. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  192. data/spec/support/protos/google_unittest_import_public.proto +41 -0
  193. data/spec/support/protos/map-test.bin +157 -0
  194. data/spec/support/protos/map-test.pb.rb +85 -0
  195. data/spec/support/protos/map-test.proto +68 -0
  196. data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
  197. data/spec/support/protos/multi_field_extensions.proto +35 -0
  198. data/spec/support/protos/resource.pb.rb +172 -0
  199. data/spec/support/protos/resource.proto +137 -0
  200. data/spec/support/resource_service.rb +23 -0
  201. data/spec/support/server.rb +65 -0
  202. data/spec/support/test_app_file.rb +2 -0
  203. data/varint_prof.rb +82 -0
  204. metadata +579 -0
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ require 'protobuf/generators/file_generator'
4
+
5
+ RSpec.describe ::Protobuf::Generators::FileGenerator do
6
+
7
+ let(:base_descriptor_fields) { { :name => 'test/foo.proto' } }
8
+ let(:descriptor_fields) { base_descriptor_fields }
9
+ let(:file_descriptor) { ::Google::Protobuf::FileDescriptorProto.new(descriptor_fields) }
10
+
11
+ subject { described_class.new(file_descriptor) }
12
+ specify { expect(subject.file_name).to eq('test/foo.pb.rb') }
13
+
14
+ describe '#print_import_requires' do
15
+ let(:descriptor_fields) do
16
+ base_descriptor_fields.merge(
17
+ :dependency => [
18
+ 'test/bar.proto',
19
+ 'test/baz.proto',
20
+ ],
21
+ )
22
+ end
23
+
24
+ it 'prints a ruby require for each dependency' do
25
+ expect(subject).to receive(:print_require).with('test/bar.pb', false)
26
+ expect(subject).to receive(:print_require).with('test/baz.pb', false)
27
+ subject.print_import_requires
28
+ end
29
+
30
+ it 'prints a ruby require_relative for each dependency if environment variable was set' do
31
+ expect(subject).to receive(:print_require).with('test/bar.pb', true)
32
+ expect(subject).to receive(:print_require).with('test/baz.pb', true)
33
+ ENV['PB_REQUIRE_RELATIVE'] = 'true'
34
+ subject.print_import_requires
35
+ ENV.delete('PB_REQUIRE_RELATIVE')
36
+ end
37
+ end
38
+
39
+ describe '#compile' do
40
+ it 'generates the file contents' do
41
+ subject.compile
42
+ expect(subject.to_s).to eq <<EOF
43
+ # encoding: utf-8
44
+
45
+ ##
46
+ # This file is auto-generated. DO NOT EDIT!
47
+ #
48
+ require 'protobuf'
49
+
50
+ EOF
51
+ end
52
+
53
+ it 'generates the file contents using default package name' do
54
+ allow(ENV).to receive(:key?).with('PB_ALLOW_DEFAULT_PACKAGE_NAME')
55
+ .and_return(true)
56
+ subject.compile
57
+ expect(subject.to_s).to eq <<EOF
58
+ # encoding: utf-8
59
+
60
+ ##
61
+ # This file is auto-generated. DO NOT EDIT!
62
+ #
63
+ require 'protobuf'
64
+
65
+ module Foo
66
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
67
+ end
68
+
69
+ EOF
70
+ end
71
+
72
+ context 'with extended messages' do
73
+ let(:descriptor_fields) do
74
+ base_descriptor_fields.merge(
75
+ :package => 'test.pkg.file_generator_spec',
76
+ :extension => [{
77
+ :name => 'boom',
78
+ :number => 20_000,
79
+ :label => Google::Protobuf::FieldDescriptorProto::Label::LABEL_OPTIONAL,
80
+ :type => Google::Protobuf::FieldDescriptorProto::Type::TYPE_STRING,
81
+ :extendee => '.google.protobuf.FieldOptions',
82
+ }],
83
+ )
84
+ end
85
+
86
+ it 'generates the file contents that include the namespaced extension name' do
87
+ subject.compile
88
+ expect(subject.to_s).to eq <<EOF
89
+ # encoding: utf-8
90
+
91
+ ##
92
+ # This file is auto-generated. DO NOT EDIT!
93
+ #
94
+ require 'protobuf'
95
+
96
+ module Test
97
+ module Pkg
98
+ module File_generator_spec
99
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
100
+
101
+ ##
102
+ # Extended Message Fields
103
+ #
104
+ class ::Google::Protobuf::FieldOptions < ::Protobuf::Message
105
+ optional :string, :".test.pkg.file_generator_spec.boom", 20000, :extension => true
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ EOF
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ require 'protobuf/generators/service_generator'
4
+
5
+ RSpec.describe ::Protobuf::Generators::ServiceGenerator do
6
+
7
+ class ::CustomMethodEnum < ::Protobuf::Enum
8
+ define :BOOM, 1
9
+ define :BAM, 2
10
+ end
11
+
12
+ class ::CustomMethodMessage < ::Protobuf::Message
13
+ optional :string, :foo, 1
14
+ end
15
+
16
+ class ::Google::Protobuf::MethodOptions < ::Protobuf::Message
17
+ optional :string, :custom_string_option, 22000, :extension => true
18
+ optional :bool, :custom_bool_option, 22001, :extension => true
19
+ optional :int32, :custom_int32_option, 22002, :extension => true
20
+ optional ::CustomMethodEnum, :custom_enum_option, 22003, :extension => true
21
+ optional ::CustomMethodMessage, :custom_message_option, 22004, :extension => true
22
+ end
23
+
24
+ let(:methods) do
25
+ [
26
+ { :name => 'Search', :input_type => 'FooRequest', :output_type => 'FooResponse' },
27
+ {
28
+ :name => 'FooBar',
29
+ :input_type => '.foo.Request',
30
+ :output_type => '.bar.Response',
31
+ :options => {
32
+ :custom_string_option => 'boom',
33
+ :custom_bool_option => true,
34
+ :custom_int32_option => 123,
35
+ :custom_message_option => CustomMethodMessage.new(:foo => 'bam'),
36
+ :custom_enum_option => CustomMethodEnum::BAM,
37
+ },
38
+ },
39
+ ]
40
+ end
41
+ let(:service_fields) do
42
+ {
43
+ :name => 'TestService',
44
+ :method => methods,
45
+ }
46
+ end
47
+
48
+ let(:service) { ::Google::Protobuf::ServiceDescriptorProto.new(service_fields) }
49
+
50
+ subject(:service_generator) { described_class.new(service) }
51
+
52
+ describe '#compile' do
53
+ let(:compiled) do
54
+ <<EOF
55
+ class TestService < ::Protobuf::Rpc::Service
56
+ rpc :search, FooRequest, FooResponse
57
+ rpc :foo_bar, ::Foo::Request, ::Bar::Response do
58
+ set_option :custom_string_option, "boom"
59
+ set_option :custom_bool_option, true
60
+ set_option :custom_int32_option, 123
61
+ set_option :custom_enum_option, ::CustomMethodEnum::BAM
62
+ set_option :custom_message_option, { :foo => "bam" }
63
+ end
64
+ end
65
+
66
+ EOF
67
+ end
68
+
69
+ it "compiles the service and it's rpc methods" do
70
+ subject.compile
71
+ expect(subject.to_s).to eq(compiled)
72
+ end
73
+
74
+ context 'with PB_USE_RAW_RPC_NAMES in the environemnt' do
75
+ let(:compiled) do
76
+ <<EOF
77
+ class TestService < ::Protobuf::Rpc::Service
78
+ rpc :Search, FooRequest, FooResponse
79
+ rpc :FooBar, ::Foo::Request, ::Bar::Response do
80
+ set_option :custom_string_option, "boom"
81
+ set_option :custom_bool_option, true
82
+ set_option :custom_int32_option, 123
83
+ set_option :custom_enum_option, ::CustomMethodEnum::BAM
84
+ set_option :custom_message_option, { :foo => "bam" }
85
+ end
86
+ end
87
+
88
+ EOF
89
+ end
90
+
91
+ before { allow(ENV).to receive(:key?).with('PB_USE_RAW_RPC_NAMES').and_return(true) }
92
+
93
+ it 'uses the raw RPC name and does not underscore it' do
94
+ subject.compile
95
+ expect(subject.to_s).to eq(compiled)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'protobuf/lifecycle'
3
+
4
+ RSpec.describe ::Protobuf::Lifecycle do
5
+ subject { described_class }
6
+
7
+ around do |example|
8
+ # this entire class is deprecated
9
+ ::Protobuf.deprecator.silence(&example)
10
+ end
11
+
12
+ before do
13
+ ::ActiveSupport::Notifications.notifier = ::ActiveSupport::Notifications::Fanout.new
14
+ end
15
+
16
+ it "registers a string as the event_name" do
17
+ expect(::ActiveSupport::Notifications).to receive(:subscribe).with("something")
18
+ subject.register("something") { true }
19
+ end
20
+
21
+ it "only registers blocks for event callbacks" do
22
+ expect do
23
+ subject.register("something")
24
+ end.to raise_error(/block/)
25
+ end
26
+
27
+ it "calls the registered block when triggered" do
28
+ this = nil
29
+ subject.register("this") do
30
+ this = "not nil"
31
+ end
32
+
33
+ subject.trigger("this")
34
+ expect(this).to_not be_nil
35
+ expect(this).to eq("not nil")
36
+ end
37
+
38
+ it "calls multiple registered blocks when triggered" do
39
+ this = nil
40
+ that = nil
41
+
42
+ subject.register("this") do
43
+ this = "not nil"
44
+ end
45
+
46
+ subject.register("this") do
47
+ that = "not nil"
48
+ end
49
+
50
+ subject.trigger("this")
51
+ expect(this).to_not be_nil
52
+ expect(this).to eq("not nil")
53
+ expect(that).to_not be_nil
54
+ expect(that).to eq("not nil")
55
+ end
56
+
57
+ context 'when the registered block has arity' do
58
+ context 'and the triggered event does not have args' do
59
+ it 'does not pass the args' do
60
+ outer_bar = nil
61
+
62
+ subject.register('foo') do |bar|
63
+ expect(bar).to be_nil
64
+ outer_bar = 'triggered'
65
+ end
66
+
67
+ subject.trigger('foo')
68
+ expect(outer_bar).to eq 'triggered'
69
+ end
70
+ end
71
+
72
+ context 'and the triggered event has arguments' do
73
+ it 'does not pass the args' do
74
+ outer_bar = nil
75
+
76
+ subject.register('foo') do |bar|
77
+ expect(bar).to_not be_nil
78
+ outer_bar = bar
79
+ end
80
+
81
+ subject.trigger('foo', 'baz')
82
+ expect(outer_bar).to eq 'baz'
83
+ end
84
+ end
85
+ end
86
+
87
+ context "normalized event names" do
88
+ specify { expect(subject.normalized_event_name(:derp)).to eq("derp") }
89
+ specify { expect(subject.normalized_event_name(:Derp)).to eq("derp") }
90
+ specify { expect(subject.normalized_event_name("DERP")).to eq("derp") }
91
+ specify { expect(subject.normalized_event_name("derp")).to eq("derp") }
92
+ end
93
+
94
+ end
@@ -0,0 +1,944 @@
1
+ # encoding: utf-8
2
+
3
+ require 'stringio'
4
+ require 'spec_helper'
5
+ require PROTOS_PATH.join('resource.pb')
6
+ require PROTOS_PATH.join('enum.pb')
7
+
8
+ RSpec.describe Protobuf::Message do
9
+
10
+ describe '.decode' do
11
+ let(:message) { ::Test::Resource.new(:name => "Jim") }
12
+
13
+ it 'creates a new message object decoded from the given bytes' do
14
+ expect(::Test::Resource.decode(message.encode)).to eq message
15
+ end
16
+
17
+ context 'with a new enum value' do
18
+ let(:older_message) do
19
+ Class.new(Protobuf::Message) do
20
+ enum_class = Class.new(::Protobuf::Enum) do
21
+ define :YAY, 1
22
+ end
23
+
24
+ optional enum_class, :enum_field, 1
25
+ repeated enum_class, :enum_list, 2
26
+ end
27
+ end
28
+
29
+ let(:newer_message) do
30
+ Class.new(Protobuf::Message) do
31
+ enum_class = Class.new(::Protobuf::Enum) do
32
+ define :YAY, 1
33
+ define :HOORAY, 2
34
+ end
35
+
36
+ optional enum_class, :enum_field, 1
37
+ repeated enum_class, :enum_list, 2
38
+ end
39
+ end
40
+
41
+ context 'with a singular field' do
42
+ it 'treats the field as if it was unset when decoding' do
43
+ newer = newer_message.new(:enum_field => :HOORAY).serialize
44
+
45
+ expect(older_message.decode(newer).enum_field!).to be_nil
46
+ end
47
+
48
+ it 'rejects an unknown value when using the constructor' do
49
+ expect { older_message.new(:enum_field => :HOORAY) }.to raise_error(TypeError)
50
+ end
51
+
52
+ it 'rejects an unknown value when the setter' do
53
+ older = older_message.new
54
+ expect { older.enum_field = :HOORAY }.to raise_error(TypeError)
55
+ end
56
+ end
57
+
58
+ context 'with a repeated field' do
59
+ it 'treats the field as if it was unset when decoding' do
60
+ newer = newer_message.new(:enum_list => [:HOORAY]).serialize
61
+
62
+ expect(older_message.decode(newer).enum_list).to eq([])
63
+ end
64
+
65
+ it 'rejects an unknown value when using the constructor' do
66
+ expect { older_message.new(:enum_list => [:HOORAY]) }.to raise_error(TypeError)
67
+ end
68
+
69
+ it 'rejects an unknown value when the setter' do
70
+ older = older_message.new
71
+ expect { older.enum_field = [:HOORAY] }.to raise_error(TypeError)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '.decode_from' do
78
+ let(:message) { ::Test::Resource.new(:name => "Jim") }
79
+
80
+ it 'creates a new message object decoded from the given byte stream' do
81
+ stream = ::StringIO.new(message.encode)
82
+ expect(::Test::Resource.decode_from(stream)).to eq message
83
+ end
84
+ end
85
+
86
+ describe 'defining a new field' do
87
+ context 'when defining a field with a tag that has already been used' do
88
+ it 'raises a TagCollisionError' do
89
+ expect do
90
+ Class.new(Protobuf::Message) do
91
+ optional ::Protobuf::Field::Int32Field, :foo, 1
92
+ optional ::Protobuf::Field::Int32Field, :bar, 1
93
+ end
94
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 1 has already been used/)
95
+ end
96
+ end
97
+
98
+ context 'when defining an extension field with a tag that has already been used' do
99
+ it 'raises a TagCollisionError' do
100
+ expect do
101
+ Class.new(Protobuf::Message) do
102
+ extensions 100...110
103
+ optional ::Protobuf::Field::Int32Field, :foo, 100
104
+ optional ::Protobuf::Field::Int32Field, :bar, 100, :extension => true
105
+ end
106
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 100 has already been used/)
107
+ end
108
+ end
109
+
110
+ context 'when defining a field with a name that has already been used' do
111
+ it 'raises a DuplicateFieldNameError' do
112
+ expect do
113
+ Class.new(Protobuf::Message) do
114
+ optional ::Protobuf::Field::Int32Field, :foo, 1
115
+ optional ::Protobuf::Field::Int32Field, :foo, 2
116
+ end
117
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
118
+ end
119
+ end
120
+
121
+ context 'when defining an extension field with a name that has already been used' do
122
+ it 'raises a DuplicateFieldNameError' do
123
+ expect do
124
+ Class.new(Protobuf::Message) do
125
+ extensions 100...110
126
+ optional ::Protobuf::Field::Int32Field, :foo, 1
127
+ optional ::Protobuf::Field::Int32Field, :foo, 100, :extension => true
128
+ end
129
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '.encode' do
135
+ let(:values) { { :name => "Jim" } }
136
+
137
+ it 'creates a new message object with the given values and returns the encoded bytes' do
138
+ expect(::Test::Resource.encode(values)).to eq ::Test::Resource.new(values).encode
139
+ end
140
+ end
141
+
142
+ describe '#initialize' do
143
+ it "defaults to the first value listed in the enum's type definition" do
144
+ test_enum = Test::EnumTestMessage.new
145
+ expect(test_enum.non_default_enum).to eq(Test::EnumTestType.enums.first)
146
+ end
147
+
148
+ it "defaults to a a value with a name" do
149
+ test_enum = Test::EnumTestMessage.new
150
+ expect(test_enum.non_default_enum.name).to eq(Test::EnumTestType.enums.first.name)
151
+ end
152
+
153
+ it "exposes the enum getter raw value through ! method" do
154
+ test_enum = Test::EnumTestMessage.new
155
+ expect(test_enum.non_default_enum!).to be_nil
156
+ end
157
+
158
+ it "exposes the enum getter raw value through ! method (when set)" do
159
+ test_enum = Test::EnumTestMessage.new
160
+ test_enum.non_default_enum = 1
161
+ expect(test_enum.non_default_enum!).to eq(1)
162
+ end
163
+
164
+ it "does not try to set attributes which have nil values" do
165
+ expect_any_instance_of(Test::EnumTestMessage).not_to receive("non_default_enum=")
166
+ Test::EnumTestMessage.new(:non_default_enum => nil)
167
+ end
168
+
169
+ it "takes a hash as an initialization argument" do
170
+ test_enum = Test::EnumTestMessage.new(:non_default_enum => 2)
171
+ expect(test_enum.non_default_enum).to eq(2)
172
+ end
173
+
174
+ it "initializes with an object that responds to #to_hash" do
175
+ hashie_object = OpenStruct.new(:to_hash => { :non_default_enum => 2 })
176
+ test_enum = Test::EnumTestMessage.new(hashie_object)
177
+ expect(test_enum.non_default_enum).to eq(2)
178
+ end
179
+
180
+ it "initializes with an object with a block" do
181
+ test_enum = Test::EnumTestMessage.new { |p| p.non_default_enum = 2 }
182
+ expect(test_enum.non_default_enum).to eq(2)
183
+ end
184
+
185
+ # to be deprecated
186
+ it "allows you to pass nil to repeated fields" do
187
+ test = Test::Resource.new(:repeated_enum => nil)
188
+ expect(test.repeated_enum).to eq([])
189
+ end
190
+ end
191
+
192
+ describe '#encode' do
193
+ context "encoding" do
194
+ it "accepts UTF-8 strings into string fields" do
195
+ message = ::Test::Resource.new(:name => "Kyle Redfearn\u0060s iPad")
196
+
197
+ expect { message.encode }.to_not raise_error
198
+ end
199
+
200
+ it "keeps utf-8 when utf-8 is input for string fields" do
201
+ name = 'my name💩'
202
+ name.force_encoding(Encoding::UTF_8)
203
+
204
+ message = ::Test::Resource.new(:name => name)
205
+ new_message = ::Test::Resource.decode(message.encode)
206
+ expect(new_message.name == name).to be true
207
+ end
208
+
209
+ it "trims binary when binary is input for string fields" do
210
+ name = "my name\xC3"
211
+ name.force_encoding(Encoding::BINARY)
212
+
213
+ message = ::Test::Resource.new(:name => name)
214
+ new_message = ::Test::Resource.decode(message.encode)
215
+ expect(new_message.name == "my name").to be true
216
+ end
217
+ end
218
+
219
+ context "when there's no value for a required field" do
220
+ let(:message) { ::Test::ResourceWithRequiredField.new }
221
+
222
+ it "raises a 'message not initialized' error" do
223
+ expect do
224
+ message.encode
225
+ end.to raise_error(Protobuf::SerializationError, /required/i)
226
+ end
227
+ end
228
+
229
+ context "repeated fields" do
230
+ let(:message) { ::Test::Resource.new(:name => "something") }
231
+
232
+ it "does not raise an error when repeated fields are []" do
233
+ expect do
234
+ message.repeated_enum = []
235
+ message.encode
236
+ end.to_not raise_error
237
+ end
238
+
239
+ it "sets the value to nil when empty array is passed" do
240
+ message.repeated_enum = []
241
+ expect(message.instance_variable_get("@values")[:repeated_enum]).to be_nil
242
+ end
243
+
244
+ it "does not compact the edit original array" do
245
+ a = [nil].freeze
246
+ message.repeated_enum = a
247
+ expect(message.repeated_enum).to eq([])
248
+ expect(a).to eq([nil].freeze)
249
+ end
250
+
251
+ it "compacts the set array" do
252
+ message.repeated_enum = [nil]
253
+ expect(message.repeated_enum).to eq([])
254
+ end
255
+
256
+ it "raises TypeError when a non-array replaces it" do
257
+ expect do
258
+ message.repeated_enum = 2
259
+ end.to raise_error(/value of type/)
260
+ end
261
+ end
262
+ end
263
+
264
+ describe "boolean predicate methods" do
265
+ subject { Test::ResourceFindRequest.new(:name => "resource") }
266
+
267
+ it { is_expected.to respond_to(:active?) }
268
+
269
+ it "sets the predicate to true when the boolean value is true" do
270
+ subject.active = true
271
+ expect(subject.active?).to be true
272
+ end
273
+
274
+ it "sets the predicate to false when the boolean value is false" do
275
+ subject.active = false
276
+ expect(subject.active?).to be false
277
+ end
278
+
279
+ it "does not put predicate methods on non-boolean fields" do
280
+ expect(Test::ResourceFindRequest.new(:name => "resource")).to_not respond_to(:name?)
281
+ end
282
+ end
283
+
284
+ describe "#respond_to_and_has?" do
285
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
286
+
287
+ it "is false when the message does not have the field" do
288
+ expect(subject.respond_to_and_has?(:other_field)).to be false
289
+ end
290
+
291
+ it "is true when the message has the field" do
292
+ expect(subject.respond_to_and_has?(:non_default_enum)).to be true
293
+ end
294
+ end
295
+
296
+ describe "#respond_to_has_and_present?" do
297
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
298
+
299
+ it "is false when the message does not have the field" do
300
+ expect(subject.respond_to_and_has_and_present?(:other_field)).to be false
301
+ end
302
+
303
+ it "is false when the field is repeated and a value is not present" do
304
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
305
+ end
306
+
307
+ it "is false when the field is repeated and the value is empty array" do
308
+ subject.repeated_enums = []
309
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
310
+ end
311
+
312
+ it "is true when the field is repeated and a value is present" do
313
+ subject.repeated_enums = [2]
314
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be true
315
+ end
316
+
317
+ it "is true when the message has the field" do
318
+ expect(subject.respond_to_and_has_and_present?(:non_default_enum)).to be true
319
+ end
320
+
321
+ context "#API" do
322
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
323
+
324
+ specify { expect(subject).to respond_to(:respond_to_and_has_and_present?) }
325
+ specify { expect(subject).to respond_to(:responds_to_and_has_and_present?) }
326
+ specify { expect(subject).to respond_to(:responds_to_has?) }
327
+ specify { expect(subject).to respond_to(:respond_to_has?) }
328
+ specify { expect(subject).to respond_to(:respond_to_has_present?) }
329
+ specify { expect(subject).to respond_to(:responds_to_has_present?) }
330
+ specify { expect(subject).to respond_to(:respond_to_and_has_present?) }
331
+ specify { expect(subject).to respond_to(:responds_to_and_has_present?) }
332
+ end
333
+
334
+ end
335
+
336
+ describe '#inspect' do
337
+ let(:klass) do
338
+ Class.new(Protobuf::Message) do |klass|
339
+ enum_class = Class.new(Protobuf::Enum) do
340
+ define :YAY, 1
341
+ end
342
+
343
+ klass.const_set(:EnumKlass, enum_class)
344
+
345
+ optional :string, :name, 1
346
+ repeated :int32, :counts, 2
347
+ optional enum_class, :enum, 3
348
+ end
349
+ end
350
+
351
+ before { stub_const('MyMessage', klass) }
352
+
353
+ it 'lists the fields' do
354
+ proto = klass.new(:name => 'wooo', :counts => [1, 2, 3], :enum => klass::EnumKlass::YAY)
355
+ expect(proto.inspect).to eq('#<MyMessage name="wooo" counts=[1, 2, 3] enum=#<Protobuf::Enum(MyMessage::EnumKlass)::YAY=1>>')
356
+ end
357
+ end
358
+
359
+ describe '#to_hash' do
360
+ context 'generating values for an ENUM field' do
361
+ it 'converts the enum to its tag representation' do
362
+ hash = Test::EnumTestMessage.new(:non_default_enum => :TWO).to_hash
363
+ expect(hash).to eq(:non_default_enum => 2)
364
+ end
365
+
366
+ it 'does not populate default values' do
367
+ hash = Test::EnumTestMessage.new.to_hash
368
+ expect(hash).to eq({})
369
+ end
370
+
371
+ it 'converts repeated enum fields to an array of the tags' do
372
+ hash = Test::EnumTestMessage.new(:repeated_enums => [:ONE, :TWO, :TWO, :ONE]).to_hash
373
+ expect(hash).to eq(:repeated_enums => [1, 2, 2, 1])
374
+ end
375
+ end
376
+
377
+ context 'generating values for a Message field' do
378
+ it 'recursively hashes field messages' do
379
+ hash = Test::Nested.new(:resource => { :name => 'Nested' }).to_hash
380
+ expect(hash).to eq(:resource => { :name => 'Nested' })
381
+ end
382
+
383
+ it 'recursively hashes a repeated set of messages' do
384
+ proto = Test::Nested.new(
385
+ :multiple_resources => [
386
+ Test::Resource.new(:name => 'Resource 1'),
387
+ Test::Resource.new(:name => 'Resource 2'),
388
+ ],
389
+ )
390
+
391
+ expect(proto.to_hash).to eq(
392
+ :multiple_resources => [
393
+ { :name => 'Resource 1' },
394
+ { :name => 'Resource 2' },
395
+ ],
396
+ )
397
+ end
398
+ end
399
+
400
+ it 'uses simple field names as keys when possible and fully qualified names otherwise' do
401
+ message = Class.new(::Protobuf::Message) do
402
+ optional :int32, :field, 1
403
+ optional :int32, :colliding_field, 2
404
+ extensions 100...200
405
+ optional :int32, :".ext.normal_ext_field", 100, :extension => true
406
+ optional :int32, :".ext.colliding_field", 101, :extension => true
407
+ optional :int32, :".ext.colliding_field2", 102, :extension => true
408
+ optional :int32, :".ext2.colliding_field2", 103, :extension => true
409
+ end
410
+
411
+ hash = {
412
+ :field => 1,
413
+ :colliding_field => 2,
414
+ :normal_ext_field => 3,
415
+ :".ext.colliding_field" => 4,
416
+ :".ext.colliding_field2" => 5,
417
+ :".ext2.colliding_field2" => 6,
418
+ }
419
+ instance = message.new(hash)
420
+ expect(instance.to_hash).to eq(hash)
421
+ end
422
+ end
423
+
424
+ describe '#to_json' do
425
+ subject do
426
+ ::Test::ResourceFindRequest.new(:name => 'Test Name', :active => false)
427
+ end
428
+
429
+ specify { expect(subject.to_json).to eq '{"name":"Test Name","active":false}' }
430
+
431
+ context 'for byte fields' do
432
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
433
+
434
+ subject do
435
+ ::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
436
+ end
437
+
438
+ specify { expect(subject.to_json).to eq '{"widget_bytes":["Bo0xSFAXOmI="]}' }
439
+ end
440
+
441
+ context 'using lower camel case field names' do
442
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
443
+
444
+ subject do
445
+ ::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
446
+ end
447
+
448
+ specify { expect(subject.to_json(:lower_camel_case => true)).to eq '{"widgetBytes":["Bo0xSFAXOmI="]}' }
449
+ end
450
+ end
451
+
452
+ describe '.from_json' do
453
+ it 'decodes optional bytes field with base64' do
454
+ expected_single_bytes = "\x06\x8D1HP\x17:b".unpack('C*')
455
+ single_bytes = ::Test::ResourceFindRequest
456
+ .from_json('{"singleBytes":"Bo0xSFAXOmI="}')
457
+ .single_bytes.unpack('C*')
458
+
459
+ expect(single_bytes).to(eq(expected_single_bytes))
460
+ end
461
+
462
+ it 'decodes repeated bytes field with base64' do
463
+ expected_widget_bytes = ["\x06\x8D1HP\x17:b"].map { |s| s.unpack('C*') }
464
+ widget_bytes = ::Test::ResourceFindRequest
465
+ .from_json('{"widgetBytes":["Bo0xSFAXOmI="]}')
466
+ .widget_bytes.map { |s| s.unpack('C*') }
467
+
468
+ expect(widget_bytes).to(eq(expected_widget_bytes))
469
+ end
470
+ end
471
+
472
+ describe '.to_json' do
473
+ it 'returns the class name of the message for use in json encoding' do
474
+ expect do
475
+ ::Timeout.timeout(0.1) do
476
+ expect(::Test::Resource.to_json).to eq("Test::Resource")
477
+ end
478
+ end.not_to raise_error
479
+ end
480
+ end
481
+
482
+ describe "#define_accessor" do
483
+ subject { ::Test::Resource.new }
484
+
485
+ it 'allows string fields to be set to nil' do
486
+ expect { subject.name = nil }.to_not raise_error
487
+ end
488
+
489
+ it 'does not allow string fields to be set to Numeric' do
490
+ expect { subject.name = 1 }.to raise_error(/name/)
491
+ end
492
+
493
+ it 'does not allow a repeated field is set to nil' do
494
+ expect { subject.repeated_enum = nil }.to raise_error(TypeError)
495
+ end
496
+
497
+ context '#{simple_field_name}!' do
498
+ it 'returns value of set field' do
499
+ expect(::Test::Resource.new(:name => "Joe").name!).to eq("Joe")
500
+ end
501
+
502
+ it 'returns value of set field with default' do
503
+ expect(::Test::Resource.new(:name => "").name!).to eq("")
504
+ end
505
+
506
+ it 'returns nil if extension field is unset' do
507
+ expect(subject.ext_is_searchable!).to be_nil
508
+ end
509
+
510
+ it 'returns value of set extension field' do
511
+ message = ::Test::Resource.new(:ext_is_searchable => true)
512
+ expect(message.ext_is_searchable!).to be(true)
513
+ end
514
+
515
+ it 'returns value of set extension field with default' do
516
+ message = ::Test::Resource.new(:ext_is_searchable => false)
517
+ expect(message.ext_is_searchable!).to be(false)
518
+ end
519
+
520
+ it 'returns nil for an unset repeated field that has only be read' do
521
+ message = ::Test::Resource.new
522
+ expect(message.repeated_enum!).to be_nil
523
+ message.repeated_enum
524
+ expect(message.repeated_enum!).to be_nil
525
+ end
526
+
527
+ it 'returns value for an unset repeated field has been read and appended to' do
528
+ message = ::Test::Resource.new
529
+ message.repeated_enum << 1
530
+ expect(message.repeated_enum!).to eq([1])
531
+ end
532
+
533
+ it 'returns value for an unset repeated field has been explicitly set' do
534
+ message = ::Test::Resource.new
535
+ message.repeated_enum = [1]
536
+ expect(message.repeated_enum!).to eq([1])
537
+ end
538
+ end
539
+ end
540
+
541
+ describe '.get_extension_field' do
542
+ it 'fetches an extension field by its tag' do
543
+ field = ::Test::Resource.get_extension_field(100)
544
+ expect(field).to be_a(::Protobuf::Field::BoolField)
545
+ expect(field.tag).to eq(100)
546
+ expect(field.name).to eq(:ext_is_searchable)
547
+ expect(field.fully_qualified_name).to eq(:'.test.Searchable.ext_is_searchable')
548
+ expect(field).to be_extension
549
+ end
550
+
551
+ it 'fetches an extension field by its symbolized name' do
552
+ expect(::Test::Resource.get_extension_field(:ext_is_searchable)).to be_a(::Protobuf::Field::BoolField)
553
+ expect(::Test::Resource.get_extension_field('ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
554
+ expect(::Test::Resource.get_extension_field(:'.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
555
+ expect(::Test::Resource.get_extension_field('.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
556
+ end
557
+
558
+ it 'returns nil when attempting to get a non-extension field' do
559
+ expect(::Test::Resource.get_extension_field(1)).to be_nil
560
+ end
561
+
562
+ it 'returns nil when field is not found' do
563
+ expect(::Test::Resource.get_extension_field(-1)).to be_nil
564
+ expect(::Test::Resource.get_extension_field(nil)).to be_nil
565
+ end
566
+ end
567
+
568
+ describe '#field?' do
569
+ it 'returns false for non-existent field' do
570
+ expect(::Test::Resource.get_field('doesnotexist')).to be_nil
571
+ expect(::Test::Resource.new.field?('doesnotexist')).to be(false)
572
+ end
573
+
574
+ it 'returns false for unset field' do
575
+ expect(::Test::Resource.get_field('name')).to be
576
+ expect(::Test::Resource.new.field?('name')).to be(false)
577
+ end
578
+
579
+ it 'returns false for unset field from tag' do
580
+ expect(::Test::Resource.get_field(1)).to be
581
+ expect(::Test::Resource.new.field?(1)).to be(false)
582
+ end
583
+
584
+ it 'returns true for set field' do
585
+ expect(::Test::Resource.new(:name => "Joe").field?('name')).to be(true)
586
+ end
587
+
588
+ it 'returns true for set field with default' do
589
+ expect(::Test::Resource.new(:name => "").field?('name')).to be(true)
590
+ end
591
+
592
+ it 'returns true from field tag value' do
593
+ expect(::Test::Resource.new(:name => "Joe").field?(1)).to be(true)
594
+ end
595
+
596
+ it 'returns false for unset extension field' do
597
+ ext_field = :".test.Searchable.ext_is_searchable"
598
+ expect(::Test::Resource.get_extension_field(ext_field)).to be
599
+ expect(::Test::Resource.new.field?(ext_field)).to be(false)
600
+ end
601
+
602
+ it 'returns false for unset extension field from tag' do
603
+ expect(::Test::Resource.get_extension_field(100)).to be
604
+ expect(::Test::Resource.new.field?(100)).to be(false)
605
+ end
606
+
607
+ it 'returns true for set extension field' do
608
+ ext_field = :".test.Searchable.ext_is_searchable"
609
+ message = ::Test::Resource.new(ext_field => true)
610
+ expect(message.field?(ext_field)).to be(true)
611
+ end
612
+
613
+ it 'returns true for set extension field with default' do
614
+ ext_field = :".test.Searchable.ext_is_searchable"
615
+ message = ::Test::Resource.new(ext_field => false)
616
+ expect(message.field?(ext_field)).to be(true)
617
+ end
618
+
619
+ it 'returns true for set extension field from tag' do
620
+ ext_field = :".test.Searchable.ext_is_searchable"
621
+ message = ::Test::Resource.new(ext_field => false)
622
+ expect(message.field?(100)).to be(true)
623
+ end
624
+
625
+ it 'returns false for repeated field that has been read from' do
626
+ message = ::Test::Resource.new
627
+ expect(message.field?(:repeated_enum)).to be(false)
628
+ message.repeated_enum
629
+ expect(message.field?(:repeated_enum)).to be(false)
630
+ end
631
+
632
+ it 'returns true for a repeated field that has been read from and appended to' do
633
+ message = ::Test::Resource.new
634
+ message.repeated_enum << 1
635
+ expect(message.field?(:repeated_enum)).to be(true)
636
+ end
637
+
638
+ it 'returns true for a repeated field that has been set with the setter' do
639
+ message = ::Test::Resource.new
640
+ message.repeated_enum = [1]
641
+ expect(message.field?(:repeated_enum)).to be(true)
642
+ end
643
+
644
+ it 'returns false for a repeated field that has been replaced with []' do
645
+ message = ::Test::Resource.new
646
+ message.repeated_enum.replace([])
647
+ expect(message.field?(:repeated_enum)).to be(false)
648
+ end
649
+ end
650
+
651
+ describe '.get_field' do
652
+ it 'fetches a non-extension field by its tag' do
653
+ field = ::Test::Resource.get_field(1)
654
+ expect(field).to be_a(::Protobuf::Field::StringField)
655
+ expect(field.tag).to eq(1)
656
+ expect(field.name).to eq(:name)
657
+ expect(field.fully_qualified_name).to eq(:name)
658
+ expect(field).not_to be_extension
659
+ end
660
+
661
+ it 'fetches a non-extension field by its symbolized name' do
662
+ expect(::Test::Resource.get_field(:name)).to be_a(::Protobuf::Field::StringField)
663
+ expect(::Test::Resource.get_field('name')).to be_a(::Protobuf::Field::StringField)
664
+ end
665
+
666
+ it 'fetches an extension field when forced' do
667
+ expect(::Test::Resource.get_field(100, true)).to be_a(::Protobuf::Field::BoolField)
668
+ expect(::Test::Resource.get_field(:'.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
669
+ expect(::Test::Resource.get_field('.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
670
+ end
671
+
672
+ it 'returns nil when attempting to get an extension field' do
673
+ expect(::Test::Resource.get_field(100)).to be_nil
674
+ end
675
+
676
+ it 'returns nil when field is not defined' do
677
+ expect(::Test::Resource.get_field(-1)).to be_nil
678
+ expect(::Test::Resource.get_field(nil)).to be_nil
679
+ end
680
+ end
681
+
682
+ describe 'defining a field' do
683
+ # Case 1
684
+ context 'single base field' do
685
+ let(:klass) do
686
+ Class.new(Protobuf::Message) do
687
+ optional :string, :foo, 1
688
+ end
689
+ end
690
+
691
+ it 'has an accessor for foo' do
692
+ message = klass.new(:foo => 'bar')
693
+ expect(message.foo).to eq('bar')
694
+ expect(message[:foo]).to eq('bar')
695
+ expect(message['foo']).to eq('bar')
696
+ end
697
+ end
698
+
699
+ # Case 2
700
+ context 'base field and extension field name collision' do
701
+ let(:klass) do
702
+ Class.new(Protobuf::Message) do
703
+ optional :string, :foo, 1
704
+ optional :string, :".boom.foo", 2, :extension => true
705
+ end
706
+ end
707
+
708
+ it 'has an accessor for foo that refers to the base field' do
709
+ message = klass.new(:foo => 'bar', '.boom.foo' => 'bam')
710
+ expect(message.foo).to eq('bar')
711
+ expect(message[:foo]).to eq('bar')
712
+ expect(message['foo']).to eq('bar')
713
+ expect(message[:'.boom.foo']).to eq('bam')
714
+ expect(message['.boom.foo']).to eq('bam')
715
+ end
716
+ end
717
+
718
+ # Case 3
719
+ context 'no base field with extension fields with name collision' do
720
+ let(:klass) do
721
+ Class.new(Protobuf::Message) do
722
+ optional :string, :".boom.foo", 2, :extension => true
723
+ optional :string, :".goat.foo", 3, :extension => true
724
+ end
725
+ end
726
+
727
+ it 'has an accessor for foo that refers to the extension field' do
728
+ message = klass.new('.boom.foo' => 'bam', '.goat.foo' => 'red')
729
+ expect { message.foo }.to raise_error(NoMethodError)
730
+ expect { message[:foo] }.to raise_error(ArgumentError)
731
+ expect { message['foo'] }.to raise_error(ArgumentError)
732
+ expect(message[:'.boom.foo']).to eq('bam')
733
+ expect(message['.boom.foo']).to eq('bam')
734
+ expect(message[:'.goat.foo']).to eq('red')
735
+ expect(message['.goat.foo']).to eq('red')
736
+ end
737
+ end
738
+
739
+ # Case 4
740
+ context 'no base field with an extension field' do
741
+ let(:klass) do
742
+ Class.new(Protobuf::Message) do
743
+ optional :string, :".boom.foo", 2, :extension => true
744
+ end
745
+ end
746
+
747
+ it 'has an accessor for foo that refers to the extension field' do
748
+ message = klass.new('.boom.foo' => 'bam')
749
+ expect(message.foo).to eq('bam')
750
+ expect(message[:foo]).to eq('bam')
751
+ expect(message['foo']).to eq('bam')
752
+ expect(message[:'.boom.foo']).to eq('bam')
753
+ expect(message['.boom.foo']).to eq('bam')
754
+ end
755
+ end
756
+ end
757
+
758
+ describe 'map fields' do
759
+ it 'serializes the same as equivalent non-map-field' do
760
+ class MessageWithMapField < ::Protobuf::Message
761
+ map :int32, :string, :map, 1
762
+ end
763
+
764
+ class MessageWithoutMapField < ::Protobuf::Message
765
+ class MapEntry < ::Protobuf::Message
766
+ optional :int32, :key, 1
767
+ optional :string, :value, 2
768
+ end
769
+ repeated MapEntry, :map, 1
770
+ end
771
+
772
+ map_msg = MessageWithMapField.new(:map =>
773
+ {
774
+ 1 => 'one',
775
+ 2 => 'two',
776
+ 3 => 'three',
777
+ 4 => 'four',
778
+ })
779
+ mapless_msg = MessageWithoutMapField.new(:map =>
780
+ [{ :key => 1, :value => 'one' },
781
+ { :key => 2, :value => 'two' },
782
+ { :key => 3, :value => 'three' },
783
+ { :key => 4, :value => 'four' },
784
+ ])
785
+
786
+ map_bytes = map_msg.encode
787
+ mapless_bytes = mapless_msg.encode
788
+ expect(map_bytes).to eq(mapless_bytes)
789
+
790
+ expect(MessageWithMapField.decode(mapless_bytes)).to eq(map_msg)
791
+ expect(MessageWithoutMapField.decode(map_bytes)).to eq(mapless_msg)
792
+ end
793
+ end
794
+
795
+ describe '.[]=' do
796
+ context 'clearing fields' do
797
+ it 'clears repeated fields with an empty array' do
798
+ instance = ::Test::Resource.new(:repeated_enum => [::Test::StatusType::ENABLED])
799
+ expect(instance.field?(:repeated_enum)).to be(true)
800
+ instance[:repeated_enum] = []
801
+ expect(instance.field?(:repeated_enum)).to be(false)
802
+ end
803
+
804
+ it 'clears optional fields with nil' do
805
+ instance = ::Test::Resource.new(:name => "Joe")
806
+ expect(instance.field?(:name)).to be(true)
807
+ instance[:name] = nil
808
+ expect(instance.field?(:name)).to be(false)
809
+ end
810
+
811
+ it 'clears optional extenstion fields with nil' do
812
+ instance = ::Test::Resource.new(:ext_is_searchable => true)
813
+ expect(instance.field?(:ext_is_searchable)).to be(true)
814
+ instance[:ext_is_searchable] = nil
815
+ expect(instance.field?(:ext_is_searchable)).to be(false)
816
+ end
817
+ end
818
+
819
+ context 'setting fields' do
820
+ let(:instance) { ::Test::Resource.new }
821
+
822
+ it 'sets and replaces repeated fields' do
823
+ initial = [::Test::StatusType::ENABLED, ::Test::StatusType::DISABLED]
824
+ instance[:repeated_enum] = initial
825
+ expect(instance[:repeated_enum]).to eq(initial)
826
+ replacement = [::Test::StatusType::DELETED]
827
+ instance[:repeated_enum] = replacement
828
+ expect(instance[:repeated_enum]).to eq(replacement)
829
+ end
830
+
831
+ it 'sets acceptable optional field values' do
832
+ instance[:name] = "Joe"
833
+ expect(instance[:name]).to eq("Joe")
834
+ instance[1] = "Tom"
835
+ expect(instance[:name]).to eq("Tom")
836
+ end
837
+
838
+ it 'sets acceptable empty string field values' do
839
+ instance[:name] = ""
840
+ expect(instance.name!).to eq("")
841
+ end
842
+
843
+ it 'sets acceptable empty message field values' do
844
+ instance = ::Test::Nested.new
845
+ instance[:resource] = {}
846
+ expect(instance.resource!).to eq(::Test::Resource.new)
847
+ end
848
+
849
+ it 'sets acceptable extension field values' do
850
+ instance[:ext_is_searchable] = true
851
+ expect(instance[:ext_is_searchable]).to eq(true)
852
+ instance[:".test.Searchable.ext_is_searchable"] = false
853
+ expect(instance[:ext_is_searchable]).to eq(false)
854
+ instance[100] = true
855
+ expect(instance[:ext_is_searchable]).to eq(true)
856
+ end
857
+
858
+ # to be deprecated
859
+ it 'does nothing when sent an empty array' do
860
+ instance[:repeated_enum] = nil
861
+ expect(instance[:repeated_enum]).to eq([])
862
+ instance[:repeated_enum] = [1, 2]
863
+ expect(instance[:repeated_enum]).to eq([1, 2])
864
+ instance[:repeated_enum] = nil
865
+ # Yes this is very silly, but backwards compatible
866
+ expect(instance[:repeated_enum]).to eq([1, 2])
867
+ end
868
+ end
869
+
870
+ context 'throwing TypeError' do
871
+ let(:instance) { ::Test::Resource.new }
872
+
873
+ it 'throws when a repeated value is set with a non array' do
874
+ expect { instance[:repeated_enum] = "string" }.to raise_error(TypeError)
875
+ end
876
+
877
+ it 'throws when a repeated value is set with an array of the wrong type' do
878
+ expect { instance[:repeated_enum] = [true, false] }.to raise_error(TypeError)
879
+ end
880
+
881
+ it 'throws when an optional value is not #acceptable?' do
882
+ expect { instance[:name] = 1 }.to raise_error(TypeError)
883
+ end
884
+ end
885
+
886
+ context 'ignoring unknown fields' do
887
+ around do |example|
888
+ orig = ::Protobuf.ignore_unknown_fields?
889
+ ::Protobuf.ignore_unknown_fields = true
890
+ example.call
891
+ ::Protobuf.ignore_unknown_fields = orig
892
+ end
893
+
894
+ context 'with valid fields' do
895
+ let(:values) { { :name => "Jim" } }
896
+
897
+ it "does not raise an error" do
898
+ expect { ::Test::Resource.new(values) }.to_not raise_error
899
+ end
900
+ end
901
+
902
+ context 'with non-existent field' do
903
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
904
+
905
+ it "does not raise an error" do
906
+ expect { ::Test::Resource.new(values) }.to_not raise_error
907
+ end
908
+ end
909
+ end
910
+
911
+ context 'not ignoring unknown fields' do
912
+ around do |example|
913
+ orig = ::Protobuf.ignore_unknown_fields?
914
+ ::Protobuf.ignore_unknown_fields = false
915
+ example.call
916
+ ::Protobuf.ignore_unknown_fields = orig
917
+ end
918
+
919
+ context 'with valid fields' do
920
+ let(:values) { { :name => "Jim" } }
921
+
922
+ it "does not raise an error" do
923
+ expect { ::Test::Resource.new(values) }.to_not raise_error
924
+ end
925
+ end
926
+
927
+ context 'with non-existent field' do
928
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
929
+
930
+ it "raises an error and mentions the erroneous field" do
931
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
932
+ end
933
+
934
+ context 'with a nil value' do
935
+ let(:values) { { :name => "Jim", :othername => nil } }
936
+
937
+ it "raises an error and mentions the erroneous field" do
938
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
939
+ end
940
+ end
941
+ end
942
+ end
943
+ end
944
+ end