prepor-protobuf 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rubocop.yml +51 -0
- data/.rubocop_todo.yml +89 -0
- data/.travis.yml +25 -0
- data/.yardopts +5 -0
- data/CHANGES.md +256 -0
- data/CONTRIBUTING.md +16 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +64 -0
- data/bin/protoc-gen-ruby +16 -0
- data/bin/rpc_server +5 -0
- data/install-protobuf.sh +28 -0
- data/lib/protobuf.rb +100 -0
- data/lib/protobuf/cli.rb +242 -0
- data/lib/protobuf/code_generator.rb +44 -0
- data/lib/protobuf/decoder.rb +73 -0
- data/lib/protobuf/deprecation.rb +112 -0
- data/lib/protobuf/descriptors.rb +3 -0
- data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +48 -0
- data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +251 -0
- data/lib/protobuf/encoder.rb +67 -0
- data/lib/protobuf/enum.rb +303 -0
- data/lib/protobuf/exceptions.rb +9 -0
- data/lib/protobuf/field.rb +74 -0
- data/lib/protobuf/field/base_field.rb +267 -0
- data/lib/protobuf/field/bool_field.rb +59 -0
- data/lib/protobuf/field/bytes_field.rb +82 -0
- data/lib/protobuf/field/double_field.rb +25 -0
- data/lib/protobuf/field/enum_field.rb +68 -0
- data/lib/protobuf/field/field_array.rb +87 -0
- data/lib/protobuf/field/fixed32_field.rb +25 -0
- data/lib/protobuf/field/fixed64_field.rb +28 -0
- data/lib/protobuf/field/float_field.rb +41 -0
- data/lib/protobuf/field/int32_field.rb +21 -0
- data/lib/protobuf/field/int64_field.rb +21 -0
- data/lib/protobuf/field/integer_field.rb +23 -0
- data/lib/protobuf/field/message_field.rb +65 -0
- data/lib/protobuf/field/sfixed32_field.rb +27 -0
- data/lib/protobuf/field/sfixed64_field.rb +28 -0
- data/lib/protobuf/field/signed_integer_field.rb +29 -0
- data/lib/protobuf/field/sint32_field.rb +21 -0
- data/lib/protobuf/field/sint64_field.rb +21 -0
- data/lib/protobuf/field/string_field.rb +34 -0
- data/lib/protobuf/field/uint32_field.rb +21 -0
- data/lib/protobuf/field/uint64_field.rb +21 -0
- data/lib/protobuf/field/varint_field.rb +73 -0
- data/lib/protobuf/generators/base.rb +70 -0
- data/lib/protobuf/generators/enum_generator.rb +41 -0
- data/lib/protobuf/generators/extension_generator.rb +27 -0
- data/lib/protobuf/generators/field_generator.rb +131 -0
- data/lib/protobuf/generators/file_generator.rb +135 -0
- data/lib/protobuf/generators/group_generator.rb +112 -0
- data/lib/protobuf/generators/message_generator.rb +98 -0
- data/lib/protobuf/generators/printable.rb +160 -0
- data/lib/protobuf/generators/service_generator.rb +26 -0
- data/lib/protobuf/lifecycle.rb +33 -0
- data/lib/protobuf/logging.rb +39 -0
- data/lib/protobuf/message.rb +193 -0
- data/lib/protobuf/message/fields.rb +133 -0
- data/lib/protobuf/message/serialization.rb +89 -0
- data/lib/protobuf/optionable.rb +23 -0
- data/lib/protobuf/rpc/buffer.rb +77 -0
- data/lib/protobuf/rpc/client.rb +168 -0
- data/lib/protobuf/rpc/connector.rb +19 -0
- data/lib/protobuf/rpc/connectors/base.rb +55 -0
- data/lib/protobuf/rpc/connectors/common.rb +176 -0
- data/lib/protobuf/rpc/connectors/socket.rb +73 -0
- data/lib/protobuf/rpc/connectors/zmq.rb +322 -0
- data/lib/protobuf/rpc/dynamic_discovery.pb.rb +49 -0
- data/lib/protobuf/rpc/env.rb +58 -0
- data/lib/protobuf/rpc/error.rb +28 -0
- data/lib/protobuf/rpc/error/client_error.rb +31 -0
- data/lib/protobuf/rpc/error/server_error.rb +43 -0
- data/lib/protobuf/rpc/middleware.rb +25 -0
- data/lib/protobuf/rpc/middleware/exception_handler.rb +36 -0
- data/lib/protobuf/rpc/middleware/logger.rb +91 -0
- data/lib/protobuf/rpc/middleware/request_decoder.rb +83 -0
- data/lib/protobuf/rpc/middleware/response_encoder.rb +88 -0
- data/lib/protobuf/rpc/middleware/runner.rb +18 -0
- data/lib/protobuf/rpc/rpc.pb.rb +55 -0
- data/lib/protobuf/rpc/server.rb +39 -0
- data/lib/protobuf/rpc/servers/socket/server.rb +123 -0
- data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
- data/lib/protobuf/rpc/servers/socket_runner.rb +40 -0
- data/lib/protobuf/rpc/servers/zmq/broker.rb +193 -0
- data/lib/protobuf/rpc/servers/zmq/server.rb +320 -0
- data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
- data/lib/protobuf/rpc/servers/zmq/worker.rb +104 -0
- data/lib/protobuf/rpc/servers/zmq_runner.rb +62 -0
- data/lib/protobuf/rpc/service.rb +179 -0
- data/lib/protobuf/rpc/service_directory.rb +253 -0
- data/lib/protobuf/rpc/service_dispatcher.rb +46 -0
- data/lib/protobuf/rpc/service_filters.rb +272 -0
- data/lib/protobuf/rpc/stat.rb +97 -0
- data/lib/protobuf/socket.rb +21 -0
- data/lib/protobuf/tasks.rb +1 -0
- data/lib/protobuf/tasks/compile.rake +61 -0
- data/lib/protobuf/version.rb +3 -0
- data/lib/protobuf/wire_type.rb +10 -0
- data/lib/protobuf/zmq.rb +21 -0
- data/proto/dynamic_discovery.proto +44 -0
- data/proto/google/protobuf/compiler/plugin.proto +147 -0
- data/proto/google/protobuf/descriptor.proto +620 -0
- data/proto/rpc.proto +62 -0
- data/protobuf.gemspec +51 -0
- data/spec/benchmark/tasks.rb +139 -0
- data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
- data/spec/data/data.bin +3 -0
- data/spec/data/types.bin +0 -0
- data/spec/encoding/all_types_spec.rb +105 -0
- data/spec/encoding/extreme_values_spec.rb +0 -0
- data/spec/functional/class_inheritance_spec.rb +52 -0
- data/spec/functional/socket_server_spec.rb +59 -0
- data/spec/functional/zmq_server_spec.rb +105 -0
- data/spec/lib/protobuf/cli_spec.rb +273 -0
- data/spec/lib/protobuf/code_generator_spec.rb +60 -0
- data/spec/lib/protobuf/enum_spec.rb +265 -0
- data/spec/lib/protobuf/field/bool_field_spec.rb +51 -0
- data/spec/lib/protobuf/field/field_array_spec.rb +69 -0
- data/spec/lib/protobuf/field/float_field_spec.rb +55 -0
- data/spec/lib/protobuf/field/int32_field_spec.rb +89 -0
- data/spec/lib/protobuf/field/string_field_spec.rb +45 -0
- data/spec/lib/protobuf/field_spec.rb +191 -0
- data/spec/lib/protobuf/generators/base_spec.rb +84 -0
- data/spec/lib/protobuf/generators/enum_generator_spec.rb +73 -0
- data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
- data/spec/lib/protobuf/generators/field_generator_spec.rb +102 -0
- data/spec/lib/protobuf/generators/file_generator_spec.rb +32 -0
- data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
- data/spec/lib/protobuf/generators/service_generator_spec.rb +46 -0
- data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
- data/spec/lib/protobuf/message_spec.rb +526 -0
- data/spec/lib/protobuf/optionable_spec.rb +46 -0
- data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
- data/spec/lib/protobuf/rpc/connector_spec.rb +26 -0
- data/spec/lib/protobuf/rpc/connectors/base_spec.rb +50 -0
- data/spec/lib/protobuf/rpc/connectors/common_spec.rb +170 -0
- data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +36 -0
- data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +117 -0
- data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
- data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
- data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
- data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +75 -0
- data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
- data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
- data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
- data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
- data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
- data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +52 -0
- data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
- data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
- data/spec/lib/protobuf/rpc/stat_spec.rb +68 -0
- data/spec/lib/protobuf_spec.rb +98 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/all.rb +6 -0
- data/spec/support/packed_field.rb +22 -0
- data/spec/support/server.rb +64 -0
- data/spec/support/test/all_types.data.bin +0 -0
- data/spec/support/test/all_types.data.txt +119 -0
- data/spec/support/test/defaults.pb.rb +27 -0
- data/spec/support/test/defaults.proto +9 -0
- data/spec/support/test/enum.pb.rb +55 -0
- data/spec/support/test/enum.proto +34 -0
- data/spec/support/test/extended.pb.rb +18 -0
- data/spec/support/test/extended.proto +10 -0
- data/spec/support/test/extreme_values.data.bin +0 -0
- data/spec/support/test/google_unittest.pb.rb +537 -0
- data/spec/support/test/google_unittest.proto +713 -0
- data/spec/support/test/google_unittest_import.pb.rb +39 -0
- data/spec/support/test/google_unittest_import.proto +64 -0
- data/spec/support/test/google_unittest_import_public.pb.rb +10 -0
- data/spec/support/test/google_unittest_import_public.proto +38 -0
- data/spec/support/test/multi_field_extensions.pb.rb +58 -0
- data/spec/support/test/multi_field_extensions.proto +33 -0
- data/spec/support/test/resource.pb.rb +119 -0
- data/spec/support/test/resource.proto +94 -0
- data/spec/support/test/resource_service.rb +23 -0
- data/spec/support/test_app_file.rb +2 -0
- metadata +502 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
require 'protobuf/rpc/service_directory'
|
|
4
|
+
|
|
5
|
+
RSpec.describe ::Protobuf::Rpc::ServiceDirectory do
|
|
6
|
+
subject { described_class.instance }
|
|
7
|
+
|
|
8
|
+
let(:echo_server) do
|
|
9
|
+
::Protobuf::Rpc::DynamicDiscovery::Server.new(
|
|
10
|
+
:uuid => 'echo',
|
|
11
|
+
:address => '127.0.0.1',
|
|
12
|
+
:port => '1111',
|
|
13
|
+
:ttl => 10,
|
|
14
|
+
:services => %w(EchoService),
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:hello_server) do
|
|
19
|
+
::Protobuf::Rpc::DynamicDiscovery::Server.new(
|
|
20
|
+
:uuid => "hello",
|
|
21
|
+
:address => '127.0.0.1',
|
|
22
|
+
:port => "1112",
|
|
23
|
+
:ttl => 10,
|
|
24
|
+
:services => %w(HelloService),
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
let(:hello_server_with_short_ttl) do
|
|
29
|
+
::Protobuf::Rpc::DynamicDiscovery::Server.new(
|
|
30
|
+
:uuid => "hello_server_with_short_ttl",
|
|
31
|
+
:address => '127.0.0.1',
|
|
32
|
+
:port => '1113',
|
|
33
|
+
:ttl => 1,
|
|
34
|
+
:services => %w(HelloService),
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
let(:combo_server) do
|
|
39
|
+
::Protobuf::Rpc::DynamicDiscovery::Server.new(
|
|
40
|
+
:uuid => "combo",
|
|
41
|
+
:address => '127.0.0.1',
|
|
42
|
+
:port => '1114',
|
|
43
|
+
:ttl => 10,
|
|
44
|
+
:services => %w(HelloService EchoService),
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
before(:all) do
|
|
49
|
+
@address = "127.0.0.1"
|
|
50
|
+
@port = 33333
|
|
51
|
+
@socket = UDPSocket.new
|
|
52
|
+
EchoService = Class.new
|
|
53
|
+
|
|
54
|
+
described_class.address = @address
|
|
55
|
+
described_class.port = @port
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def expect_event_trigger(event)
|
|
59
|
+
expect(::ActiveSupport::Notifications).to receive(:instrument)
|
|
60
|
+
.with(event, hash_including(:listing => an_instance_of(::Protobuf::Rpc::ServiceDirectory::Listing))).once
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def send_beacon(type, server)
|
|
64
|
+
type = type.to_s.upcase
|
|
65
|
+
beacon = ::Protobuf::Rpc::DynamicDiscovery::Beacon.new(
|
|
66
|
+
:server => server,
|
|
67
|
+
:beacon_type => ::Protobuf::Rpc::DynamicDiscovery::BeaconType.fetch(type),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@socket.send(beacon.encode, 0, @address, @port)
|
|
71
|
+
sleep 0.01 # give the service directory time to process the beacon
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should be a singleton" do
|
|
75
|
+
expect(subject).to be_a_kind_of(Singleton)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should be configured to listen to address 127.0.0.1" do
|
|
79
|
+
expect(described_class.address).to eq '127.0.0.1'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "should be configured to listen to port 33333" do
|
|
83
|
+
expect(described_class.port).to eq 33333
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should defer .start to the instance#start" do
|
|
87
|
+
expect(described_class.instance).to receive(:start)
|
|
88
|
+
described_class.start
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should yeild itself to blocks passed to .start" do
|
|
92
|
+
allow(described_class.instance).to receive(:start)
|
|
93
|
+
expect { |b| described_class.start(&b) }.to yield_with_args(described_class)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should defer .stop to the instance#stop" do
|
|
97
|
+
expect(described_class.instance).to receive(:stop)
|
|
98
|
+
described_class.stop
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "stopped" do
|
|
102
|
+
before { subject.stop }
|
|
103
|
+
|
|
104
|
+
describe "#lookup" do
|
|
105
|
+
it "should return nil" do
|
|
106
|
+
send_beacon(:heartbeat, echo_server)
|
|
107
|
+
expect(subject.lookup("EchoService")).to be_nil
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "#restart" do
|
|
112
|
+
it "should start the service" do
|
|
113
|
+
subject.restart
|
|
114
|
+
expect(subject).to be_running
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
describe "#running" do
|
|
119
|
+
it "should be false" do
|
|
120
|
+
expect(subject).to_not be_running
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
describe "#stop" do
|
|
125
|
+
it "has no effect" do
|
|
126
|
+
subject.stop
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "started" do
|
|
132
|
+
before { subject.start }
|
|
133
|
+
after { subject.stop }
|
|
134
|
+
|
|
135
|
+
specify { expect(subject).to be_running }
|
|
136
|
+
|
|
137
|
+
it "should trigger added events" do
|
|
138
|
+
expect_event_trigger("directory.listing.added")
|
|
139
|
+
send_beacon(:heartbeat, echo_server)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should trigger updated events" do
|
|
143
|
+
send_beacon(:heartbeat, echo_server)
|
|
144
|
+
expect_event_trigger("directory.listing.updated")
|
|
145
|
+
send_beacon(:heartbeat, echo_server)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "should trigger removed events" do
|
|
149
|
+
send_beacon(:heartbeat, echo_server)
|
|
150
|
+
expect_event_trigger("directory.listing.removed")
|
|
151
|
+
send_beacon(:flatline, echo_server)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
describe "#all_listings_for" do
|
|
155
|
+
context "when listings are present" do
|
|
156
|
+
it "returns all listings for a given service" do
|
|
157
|
+
send_beacon(:heartbeat, hello_server)
|
|
158
|
+
send_beacon(:heartbeat, combo_server)
|
|
159
|
+
|
|
160
|
+
expect(subject.all_listings_for("HelloService").size).to eq(2)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "when no listings are present" do
|
|
165
|
+
it "returns and empty array" do
|
|
166
|
+
expect(subject.all_listings_for("HelloService").size).to eq(0)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe "#each_listing" do
|
|
172
|
+
it "should yield to a block for each listing" do
|
|
173
|
+
send_beacon(:heartbeat, hello_server)
|
|
174
|
+
send_beacon(:heartbeat, echo_server)
|
|
175
|
+
send_beacon(:heartbeat, combo_server)
|
|
176
|
+
|
|
177
|
+
expect do |block|
|
|
178
|
+
subject.each_listing(&block)
|
|
179
|
+
end.to yield_control.exactly(3).times
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
describe "#lookup" do
|
|
184
|
+
it "should provide listings by service" do
|
|
185
|
+
send_beacon(:heartbeat, hello_server)
|
|
186
|
+
expect(subject.lookup("HelloService").to_hash).to eq hello_server.to_hash
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "should return random listings" do
|
|
190
|
+
send_beacon(:heartbeat, hello_server)
|
|
191
|
+
send_beacon(:heartbeat, combo_server)
|
|
192
|
+
|
|
193
|
+
uuids = 100.times.map { subject.lookup("HelloService").uuid }
|
|
194
|
+
expect(uuids.count("hello")).to be_within(25).of(50)
|
|
195
|
+
expect(uuids.count("combo")).to be_within(25).of(50)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "should not return expired listings" do
|
|
199
|
+
send_beacon(:heartbeat, hello_server_with_short_ttl)
|
|
200
|
+
sleep 5
|
|
201
|
+
expect(subject.lookup("HelloService")).to be_nil
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "should not return flatlined servers" do
|
|
205
|
+
send_beacon(:heartbeat, echo_server)
|
|
206
|
+
send_beacon(:heartbeat, combo_server)
|
|
207
|
+
send_beacon(:flatline, echo_server)
|
|
208
|
+
|
|
209
|
+
uuids = 100.times.map { subject.lookup("EchoService").uuid }
|
|
210
|
+
expect(uuids.count("combo")).to eq 100
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "should return up-to-date listings" do
|
|
214
|
+
send_beacon(:heartbeat, echo_server)
|
|
215
|
+
echo_server.port = "7777"
|
|
216
|
+
send_beacon(:heartbeat, echo_server)
|
|
217
|
+
|
|
218
|
+
expect(subject.lookup("EchoService").port).to eq "7777"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
context 'when given service identifier is a class name' do
|
|
222
|
+
it 'returns the listing corresponding to the class name' do
|
|
223
|
+
send_beacon(:heartbeat, echo_server)
|
|
224
|
+
expect(subject.lookup(EchoService).uuid).to eq echo_server.uuid
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
describe "#restart" do
|
|
230
|
+
it "should clear all listings" do
|
|
231
|
+
send_beacon(:heartbeat, echo_server)
|
|
232
|
+
send_beacon(:heartbeat, combo_server)
|
|
233
|
+
subject.restart
|
|
234
|
+
expect(subject.lookup("EchoService")).to be_nil
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe "#running" do
|
|
239
|
+
it "should be true" do
|
|
240
|
+
expect(subject).to be_running
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
describe "#stop" do
|
|
245
|
+
it "should clear all listings" do
|
|
246
|
+
send_beacon(:heartbeat, echo_server)
|
|
247
|
+
send_beacon(:heartbeat, combo_server)
|
|
248
|
+
subject.stop
|
|
249
|
+
expect(subject.lookup("EchoService")).to be_nil
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "should stop the server" do
|
|
253
|
+
subject.stop
|
|
254
|
+
expect(subject).to_not be_running
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
if ENV.key?("BENCH")
|
|
260
|
+
context "performance" do
|
|
261
|
+
let(:servers) do
|
|
262
|
+
100.times.map do |x|
|
|
263
|
+
::Protobuf::Rpc::DynamicDiscovery::Server.new(
|
|
264
|
+
:uuid => "performance_server#{x + 1}",
|
|
265
|
+
:address => '127.0.0.1',
|
|
266
|
+
:port => (5555 + x).to_s,
|
|
267
|
+
:ttl => rand(1..5),
|
|
268
|
+
:services => 10.times.map { |y| "PerformanceService#{y}" },
|
|
269
|
+
)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
before do
|
|
274
|
+
require 'benchmark'
|
|
275
|
+
subject.start
|
|
276
|
+
servers.each { |server| send_beacon(:heartbeat, server) }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
after do
|
|
280
|
+
subject.stop
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "should perform lookups in constant time" do
|
|
284
|
+
print "\n\n"
|
|
285
|
+
Benchmark.bm(17) do |x|
|
|
286
|
+
x.report(" 1_000 lookups:") { 1_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
|
|
287
|
+
x.report(" 10_000 lookups:") { 10_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
|
|
288
|
+
x.report("100_000 lookups:") { 100_000.times { subject.lookup("PerformanceService#{rand(0..9)}") } }
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'protobuf/rpc/service_dispatcher'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Protobuf::Rpc::ServiceDispatcher do
|
|
5
|
+
let(:app) { proc { |env| env } }
|
|
6
|
+
let(:env) do
|
|
7
|
+
Protobuf::Rpc::Env.new(
|
|
8
|
+
'method_name' => method_name,
|
|
9
|
+
'request' => request,
|
|
10
|
+
'rpc_service' => service_class,
|
|
11
|
+
'service_name' => service_name,
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
let(:method_name) { :find }
|
|
15
|
+
let(:request) { request_type.new(:name => 'required') }
|
|
16
|
+
let(:request_type) { service_class.rpcs[method_name].request_type }
|
|
17
|
+
let(:response) { response_type.new(:name => 'required') }
|
|
18
|
+
let(:response_type) { service_class.rpcs[method_name].response_type }
|
|
19
|
+
|
|
20
|
+
let(:rpc_service) { service_class.new(env) }
|
|
21
|
+
let(:service_class) { Test::ResourceService }
|
|
22
|
+
let(:service_name) { service_class.to_s }
|
|
23
|
+
|
|
24
|
+
subject { described_class.new(app) }
|
|
25
|
+
|
|
26
|
+
before { allow(subject).to receive(:rpc_service).and_return(rpc_service) }
|
|
27
|
+
|
|
28
|
+
describe '#call' do
|
|
29
|
+
before { allow(rpc_service).to receive(:response).and_return(response) }
|
|
30
|
+
|
|
31
|
+
it "dispatches the request" do
|
|
32
|
+
stack_env = subject.call(env)
|
|
33
|
+
expect(stack_env.response).to eq response
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "when the given RPC method is not implemented" do
|
|
37
|
+
let(:method_name) { :find_not_implemented }
|
|
38
|
+
|
|
39
|
+
it "raises a method not found exception" do
|
|
40
|
+
expect { subject.call(env) }.to raise_exception(Protobuf::Rpc::MethodNotFound)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "when the given RPC method is implemented and a NoMethodError is raised" do
|
|
45
|
+
before { allow(rpc_service).to receive(:callable_rpc_method).and_return(-> { rpc_service.__send__(:foo) }) }
|
|
46
|
+
|
|
47
|
+
it "raises the exeception" do
|
|
48
|
+
expect { subject.call(env) }.to raise_exception(NoMethodError)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class FilterTest
|
|
4
|
+
include Protobuf::Rpc::ServiceFilters
|
|
5
|
+
|
|
6
|
+
attr_accessor :called
|
|
7
|
+
|
|
8
|
+
# Initialize the hash keys as instance vars
|
|
9
|
+
def initialize(ivar_hash)
|
|
10
|
+
@called = []
|
|
11
|
+
ivar_hash.each_pair do |key, value|
|
|
12
|
+
self.class.class_eval do
|
|
13
|
+
attr_accessor key
|
|
14
|
+
end
|
|
15
|
+
__send__("#{key}=", value)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def endpoint
|
|
20
|
+
@called << :endpoint
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.clear_filters!
|
|
24
|
+
@defined_filters = nil
|
|
25
|
+
@filters = nil
|
|
26
|
+
@rescue_filters = nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
RSpec.describe Protobuf::Rpc::ServiceFilters do
|
|
31
|
+
let(:params) { {} }
|
|
32
|
+
subject { FilterTest.new(params) }
|
|
33
|
+
after(:each) { FilterTest.clear_filters! }
|
|
34
|
+
|
|
35
|
+
describe '#before_filter' do
|
|
36
|
+
let(:params) { { :before_filter_calls => 0 } }
|
|
37
|
+
|
|
38
|
+
before(:all) do
|
|
39
|
+
class FilterTest
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def verify_before
|
|
43
|
+
@called << :verify_before
|
|
44
|
+
@before_filter_calls += 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def foo
|
|
48
|
+
@called << :foo
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
before do
|
|
54
|
+
FilterTest.before_filter(:verify_before)
|
|
55
|
+
FilterTest.before_filter(:verify_before)
|
|
56
|
+
FilterTest.before_filter(:foo)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
specify { expect(subject.class).to respond_to(:before_filter) }
|
|
60
|
+
specify { expect(subject.class).to respond_to(:before_action) }
|
|
61
|
+
|
|
62
|
+
it 'calls filters in the order they were defined' do
|
|
63
|
+
subject.__send__(:run_filters, :endpoint)
|
|
64
|
+
expect(subject.called).to eq [:verify_before, :foo, :endpoint]
|
|
65
|
+
expect(subject.before_filter_calls).to eq 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context 'when filter is configured with "only"' do
|
|
69
|
+
before(:all) do
|
|
70
|
+
class FilterTest
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def endpoint_with_verify
|
|
74
|
+
@called << :endpoint_with_verify
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
before do
|
|
80
|
+
FilterTest.clear_filters!
|
|
81
|
+
FilterTest.before_filter(:verify_before, :only => :endpoint_with_verify)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
context 'when invoking a method defined in "only" option' do
|
|
85
|
+
it 'invokes the filter' do
|
|
86
|
+
subject.__send__(:run_filters, :endpoint_with_verify)
|
|
87
|
+
expect(subject.called).to eq [:verify_before, :endpoint_with_verify]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context 'when invoking a method not defined by "only" option' do
|
|
92
|
+
it 'does not invoke the filter' do
|
|
93
|
+
subject.__send__(:run_filters, :endpoint)
|
|
94
|
+
expect(subject.called).to eq [:endpoint]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context 'when filter is configured with "except"' do
|
|
100
|
+
before(:all) do
|
|
101
|
+
class FilterTest
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
def endpoint_without_verify
|
|
105
|
+
@called << :endpoint_without_verify
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
before do
|
|
111
|
+
FilterTest.clear_filters!
|
|
112
|
+
FilterTest.before_filter(:verify_before, :except => :endpoint_without_verify)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
context 'when invoking a method not defined in "except" option' do
|
|
116
|
+
it 'invokes the filter' do
|
|
117
|
+
subject.__send__(:run_filters, :endpoint)
|
|
118
|
+
expect(subject.called).to eq [:verify_before, :endpoint]
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context 'when invoking a method defined by "except" option' do
|
|
123
|
+
it 'does not invoke the filter' do
|
|
124
|
+
subject.__send__(:run_filters, :endpoint_without_verify)
|
|
125
|
+
expect(subject.called).to eq [:endpoint_without_verify]
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'when filter is configured with "if"' do
|
|
131
|
+
before(:all) do
|
|
132
|
+
class FilterTest
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def check_true
|
|
136
|
+
true
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def check_false
|
|
140
|
+
false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def verify_before
|
|
144
|
+
@called << :verify_before
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
context 'when "if" option is a method that returns true' do
|
|
150
|
+
before do
|
|
151
|
+
FilterTest.clear_filters!
|
|
152
|
+
FilterTest.before_filter(:verify_before, :if => :check_true)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it 'invokes the filter' do
|
|
156
|
+
subject.__send__(:run_filters, :endpoint)
|
|
157
|
+
expect(subject.called).to eq [:verify_before, :endpoint]
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'when "if" option is a callable that returns true' do
|
|
162
|
+
before do
|
|
163
|
+
FilterTest.clear_filters!
|
|
164
|
+
FilterTest.before_filter(:verify_before, :if => ->(_service) { true })
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it 'invokes the filter' do
|
|
168
|
+
subject.__send__(:run_filters, :endpoint)
|
|
169
|
+
expect(subject.called).to eq [:verify_before, :endpoint]
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
context 'when "if" option is a method that returns false' do
|
|
174
|
+
before do
|
|
175
|
+
FilterTest.clear_filters!
|
|
176
|
+
FilterTest.before_filter(:verify_before, :if => :check_false)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'skips the filter' do
|
|
180
|
+
subject.__send__(:run_filters, :endpoint)
|
|
181
|
+
expect(subject.called).to eq [:endpoint]
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context 'when "if" option is a callable that returns false' do
|
|
186
|
+
before do
|
|
187
|
+
FilterTest.clear_filters!
|
|
188
|
+
FilterTest.before_filter(:verify_before, :if => ->(_service) { false })
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'skips the filter' do
|
|
192
|
+
subject.__send__(:run_filters, :endpoint)
|
|
193
|
+
expect(subject.called).to eq [:endpoint]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context 'when filter is configured with "unless"' do
|
|
199
|
+
before(:all) do
|
|
200
|
+
class FilterTest
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
def check_true
|
|
204
|
+
true
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def check_false
|
|
208
|
+
false
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def verify_before
|
|
212
|
+
@called << :verify_before
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
context 'when "unless" option is a method that returns false' do
|
|
218
|
+
before do
|
|
219
|
+
FilterTest.clear_filters!
|
|
220
|
+
FilterTest.before_filter(:verify_before, :unless => :check_false)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'invokes the filter' do
|
|
224
|
+
subject.__send__(:run_filters, :endpoint)
|
|
225
|
+
expect(subject.called).to eq [:verify_before, :endpoint]
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
context 'when "unless" option is a callable that returns true' do
|
|
230
|
+
before do
|
|
231
|
+
FilterTest.clear_filters!
|
|
232
|
+
FilterTest.before_filter(:verify_before, :unless => ->(_service) { false })
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it 'invokes the filter' do
|
|
236
|
+
subject.__send__(:run_filters, :endpoint)
|
|
237
|
+
expect(subject.called).to eq [:verify_before, :endpoint]
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
context 'when "unless" option is a method that returns false' do
|
|
242
|
+
before do
|
|
243
|
+
FilterTest.clear_filters!
|
|
244
|
+
FilterTest.before_filter(:verify_before, :unless => :check_true)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it 'skips the filter' do
|
|
248
|
+
subject.__send__(:run_filters, :endpoint)
|
|
249
|
+
expect(subject.called).to eq [:endpoint]
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
context 'when "unless" option is a callable that returns false' do
|
|
254
|
+
before do
|
|
255
|
+
FilterTest.clear_filters!
|
|
256
|
+
FilterTest.before_filter(:verify_before, :unless => ->(_service) { true })
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it 'skips the filter' do
|
|
260
|
+
subject.__send__(:run_filters, :endpoint)
|
|
261
|
+
expect(subject.called).to eq [:endpoint]
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
context 'when filter returns false' do
|
|
267
|
+
before(:all) do
|
|
268
|
+
class FilterTest
|
|
269
|
+
private
|
|
270
|
+
|
|
271
|
+
def short_circuit_filter
|
|
272
|
+
@called << :short_circuit_filter
|
|
273
|
+
false
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
before do
|
|
279
|
+
FilterTest.clear_filters!
|
|
280
|
+
FilterTest.before_filter(:short_circuit_filter)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it 'does not invoke the rpc method' do
|
|
284
|
+
expect(subject).not_to receive(:endpoint)
|
|
285
|
+
subject.__send__(:run_filters, :endpoint)
|
|
286
|
+
expect(subject.called).to eq [:short_circuit_filter]
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
describe '#after_filter' do
|
|
292
|
+
let(:params) { { :after_filter_calls => 0 } }
|
|
293
|
+
|
|
294
|
+
before(:all) do
|
|
295
|
+
class FilterTest
|
|
296
|
+
private
|
|
297
|
+
|
|
298
|
+
def verify_after
|
|
299
|
+
@called << :verify_after
|
|
300
|
+
@after_filter_calls += 1
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def foo
|
|
304
|
+
@called << :foo
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
before do
|
|
310
|
+
FilterTest.after_filter(:verify_after)
|
|
311
|
+
FilterTest.after_filter(:verify_after)
|
|
312
|
+
FilterTest.after_filter(:foo)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
specify { expect(subject.class).to respond_to(:after_filter) }
|
|
316
|
+
specify { expect(subject.class).to respond_to(:after_action) }
|
|
317
|
+
|
|
318
|
+
it 'calls filters in the order they were defined' do
|
|
319
|
+
subject.__send__(:run_filters, :endpoint)
|
|
320
|
+
expect(subject.called).to eq [:endpoint, :verify_after, :foo]
|
|
321
|
+
expect(subject.after_filter_calls).to eq 1
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
describe '#around_filter' do
|
|
326
|
+
let(:params) { {} }
|
|
327
|
+
|
|
328
|
+
before(:all) do
|
|
329
|
+
class FilterTest
|
|
330
|
+
private
|
|
331
|
+
|
|
332
|
+
def outer_around
|
|
333
|
+
@called << :outer_around_top
|
|
334
|
+
yield
|
|
335
|
+
@called << :outer_around_bottom
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def inner_around
|
|
339
|
+
@called << :inner_around_top
|
|
340
|
+
yield
|
|
341
|
+
@called << :inner_around_bottom
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
before do
|
|
347
|
+
FilterTest.around_filter(:outer_around)
|
|
348
|
+
FilterTest.around_filter(:inner_around)
|
|
349
|
+
FilterTest.around_filter(:outer_around)
|
|
350
|
+
FilterTest.around_filter(:inner_around)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
specify { expect(subject.class).to respond_to(:around_filter) }
|
|
354
|
+
specify { expect(subject.class).to respond_to(:around_action) }
|
|
355
|
+
|
|
356
|
+
it 'calls filters in the order they were defined' do
|
|
357
|
+
subject.__send__(:run_filters, :endpoint)
|
|
358
|
+
expect(subject.called).to eq(
|
|
359
|
+
[
|
|
360
|
+
:outer_around_top,
|
|
361
|
+
:inner_around_top,
|
|
362
|
+
:endpoint,
|
|
363
|
+
:inner_around_bottom,
|
|
364
|
+
:outer_around_bottom,
|
|
365
|
+
],
|
|
366
|
+
)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
context 'when around_filter does not yield' do
|
|
370
|
+
before do
|
|
371
|
+
class FilterTest
|
|
372
|
+
private
|
|
373
|
+
|
|
374
|
+
def inner_around
|
|
375
|
+
@called << :inner_around
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
before do
|
|
381
|
+
FilterTest.around_filter(:outer_around)
|
|
382
|
+
FilterTest.around_filter(:inner_around)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it 'cancels calling the rest of the filters and the endpoint' do
|
|
386
|
+
expect(subject).not_to receive(:endpoint)
|
|
387
|
+
subject.__send__(:run_filters, :endpoint)
|
|
388
|
+
expect(subject.called).to eq(
|
|
389
|
+
[
|
|
390
|
+
:outer_around_top,
|
|
391
|
+
:inner_around,
|
|
392
|
+
:outer_around_bottom,
|
|
393
|
+
],
|
|
394
|
+
)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
describe '#rescue_from' do
|
|
401
|
+
before do
|
|
402
|
+
class CustomError1 < StandardError; end
|
|
403
|
+
class CustomError2 < StandardError; end
|
|
404
|
+
class CustomError3 < StandardError; end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
before do
|
|
408
|
+
class FilterTest
|
|
409
|
+
private
|
|
410
|
+
|
|
411
|
+
def filter_with_error1
|
|
412
|
+
@called << :filter_with_error1
|
|
413
|
+
fail CustomError1, 'Filter 1 failed'
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def filter_with_error2
|
|
417
|
+
@called << :filter_with_error2
|
|
418
|
+
fail CustomError1, 'Filter 2 failed'
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def filter_with_error3
|
|
422
|
+
@called << :filter_with_error3
|
|
423
|
+
fail CustomError3, 'Filter 3 failed'
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def filter_with_runtime_error
|
|
427
|
+
@called << :filter_with_runtime_error
|
|
428
|
+
fail 'Filter with runtime error failed'
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def custom_error_occurred(ex)
|
|
432
|
+
@ex_class = ex.class
|
|
433
|
+
@called << :custom_error_occurred
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
let(:params) { { :ex_class => nil } }
|
|
439
|
+
|
|
440
|
+
context 'when defining multiple errors with a given callback' do
|
|
441
|
+
before do
|
|
442
|
+
FilterTest.rescue_from(CustomError1, CustomError2, CustomError3, :with => :custom_error_occurred)
|
|
443
|
+
end
|
|
444
|
+
before { FilterTest.before_filter(:filter_with_error3) }
|
|
445
|
+
|
|
446
|
+
it 'short-circuits the call stack' do
|
|
447
|
+
expect do
|
|
448
|
+
expect(subject).not_to receive(:endpoint)
|
|
449
|
+
subject.__send__(:run_filters, :endpoint)
|
|
450
|
+
expect(subject.called).to eq([:filter_with_error3, :custom_error_occurred])
|
|
451
|
+
expect(subject.ex_class).to eq CustomError3
|
|
452
|
+
end.not_to raise_error
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
context 'when defined with options' do
|
|
457
|
+
context 'when :with option is not given' do
|
|
458
|
+
specify do
|
|
459
|
+
expect { FilterTest.rescue_from(CustomError1) }.to raise_error(ArgumentError, /with/)
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
context 'when error occurs inside filter' do
|
|
464
|
+
before { FilterTest.rescue_from(CustomError1, :with => :custom_error_occurred) }
|
|
465
|
+
before { FilterTest.before_filter(:filter_with_error1) }
|
|
466
|
+
|
|
467
|
+
it 'short-circuits the call stack' do
|
|
468
|
+
expect do
|
|
469
|
+
expect(subject).not_to receive(:endpoint)
|
|
470
|
+
subject.__send__(:run_filters, :endpoint)
|
|
471
|
+
expect(subject.called).to eq([:filter_with_error1, :custom_error_occurred])
|
|
472
|
+
expect(subject.ex_class).to eq CustomError1
|
|
473
|
+
end.not_to raise_error
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
context 'when defined with block' do
|
|
479
|
+
before do
|
|
480
|
+
FilterTest.rescue_from(CustomError1) do |service, ex|
|
|
481
|
+
service.ex_class = ex.class
|
|
482
|
+
service.called << :block_rescue_handler
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
before { FilterTest.before_filter(:filter_with_error1) }
|
|
486
|
+
|
|
487
|
+
it 'short-circuits the call stack' do
|
|
488
|
+
expect do
|
|
489
|
+
expect(subject).not_to receive(:endpoint)
|
|
490
|
+
subject.__send__(:run_filters, :endpoint)
|
|
491
|
+
expect(subject.called).to eq([:filter_with_error1, :block_rescue_handler])
|
|
492
|
+
expect(subject.ex_class).to eq CustomError1
|
|
493
|
+
end.not_to raise_error
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
context 'when thrown exception inherits from a mapped exception' do
|
|
498
|
+
before do
|
|
499
|
+
FilterTest.rescue_from(StandardError) do |service, ex|
|
|
500
|
+
service.ex_class = ex.class
|
|
501
|
+
service.called << :standard_error_rescue_handler
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
before { FilterTest.before_filter(:filter_with_runtime_error) }
|
|
505
|
+
|
|
506
|
+
it 'rescues with the given callable' do
|
|
507
|
+
expect do
|
|
508
|
+
expect(subject).not_to receive(:endpoint)
|
|
509
|
+
subject.__send__(:run_filters, :endpoint)
|
|
510
|
+
expect(subject.called).to eq([:filter_with_runtime_error, :standard_error_rescue_handler])
|
|
511
|
+
expect(subject.ex_class).to eq RuntimeError
|
|
512
|
+
end.not_to raise_error
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
end
|