beetle 1.0.3 → 2.0.0rc1

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.
@@ -1,5 +0,0 @@
1
- #!/bin/bash
2
- ENV="RAINBOW_COLORED_TESTS=$RAINBOW_COLORED_TESTS"
3
- cmd="docker run -it --rm -e $ENV beetle-test"
4
- echo $cmd
5
- exec $cmd
@@ -1,118 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
- module Beetle
4
- class RedisConfigurationClientTest < Minitest::Test
5
- def setup
6
- Beetle.config.redis_servers = "redis:0,redis:1"
7
- @client = RedisConfigurationClient.new
8
- @client.stubs(:touch_master_file)
9
- @client.stubs(:verify_redis_master_file_string)
10
- end
11
-
12
- test "should send a client_started message when started" do
13
- @client.stubs(:clear_redis_master_file)
14
- @client.beetle.expects(:publish).with(:client_started, {:id => @client.id}.to_json)
15
- Client.any_instance.stubs(:listen)
16
- @client.start
17
- end
18
-
19
- test "should install a periodic timer which sends a heartbeat message" do
20
- @client.stubs(:clear_redis_master_file)
21
- @client.beetle.expects(:publish).with(:client_started, {:id => @client.id}.to_json)
22
- @client.beetle.expects(:listen).yields
23
- EventMachine.expects(:add_periodic_timer).with(Beetle.config.redis_failover_client_heartbeat_interval).yields
24
- @client.beetle.expects(:publish).with(:heartbeat, {:id => @client.id}.to_json)
25
- @client.start
26
- end
27
-
28
- test "config should return the beetle config" do
29
- assert_equal Beetle.config, @client.config
30
- end
31
-
32
- test "ping message should answer with pong" do
33
- @client.expects(:pong!)
34
- @client.ping("token" => 1)
35
- end
36
-
37
- test "pong should publish a pong message" do
38
- @client.beetle.expects(:publish)
39
- @client.send(:pong!)
40
- end
41
-
42
- test "invalidation should send an invalidation message and clear the redis master file" do
43
- @client.expects(:clear_redis_master_file)
44
- @client.beetle.expects(:publish).with(:client_invalidated, anything)
45
- @client.send(:invalidate!)
46
- end
47
-
48
- test "should ignore outdated invalidate messages" do
49
- new_payload = {"token" => 2}
50
- old_payload = {"token" => 1}
51
-
52
- @client.expects(:invalidate!).once
53
-
54
- @client.invalidate(new_payload)
55
- @client.invalidate(old_payload)
56
- end
57
-
58
- test "should ignore invalidate messages when current master is still a master" do
59
- @client.instance_variable_set :@current_master, stub(:master? => true)
60
- @client.expects(:invalidate!).never
61
- @client.invalidate("token" => 1)
62
- end
63
-
64
- test "should ignore outdated reconfigure messages" do
65
- new_payload = {"token" => 2, "server" => "master:2"}
66
- old_payload = {"token" => 1, "server" => "master:1"}
67
- @client.stubs(:read_redis_master_file).returns("")
68
-
69
- @client.expects(:write_redis_master_file).once
70
-
71
- @client.reconfigure(new_payload)
72
- @client.reconfigure(old_payload)
73
- end
74
-
75
- test "should clear redis master file if redis from master file is slave" do
76
- @client.stubs(:redis_master_from_master_file).returns(stub(:master? => false))
77
- Beetle::Client.any_instance.stubs(:publish)
78
- @client.expects(:clear_redis_master_file)
79
- @client.expects(:client_started!)
80
- Client.any_instance.stubs(:listen)
81
- @client.start
82
- end
83
-
84
- test "should clear redis master file if redis from master file is not available" do
85
- @client.stubs(:redis_master_from_master_file).returns(nil)
86
- Beetle::Client.any_instance.stubs(:publish)
87
- @client.expects(:clear_redis_master_file)
88
- @client.expects(:client_started!)
89
- Client.any_instance.stubs(:listen)
90
- @client.start
91
- end
92
-
93
- test "the dispatcher should just forward messages to the client" do
94
- dispatcher_class = RedisConfigurationClient.class_eval "MessageDispatcher"
95
- dispatcher_class.configuration_client = @client
96
- dispatcher = dispatcher_class.new
97
- payload = {"token" => 1}
98
- dispatcher.stubs(:message).returns(stub(:data => payload.to_json, :header => stub(:routing_key=> "ping")))
99
- @client.expects(:ping).with(payload)
100
- dispatcher.send(:process)
101
- end
102
-
103
- test "determine_initial_master should return nil if there is no file" do
104
- @client.expects(:master_file_exists?).returns(false)
105
- assert_nil @client.send(:determine_initial_master)
106
- assert_nil @client.current_master
107
- end
108
-
109
- test "determine_initial_master should instantiate a new redis if there is a file with content" do
110
- @client.expects(:master_file_exists?).returns(true)
111
- @client.expects(:read_redis_master_file).returns("localhost:6379")
112
- master = @client.send(:determine_initial_master)
113
- assert_equal "master", master.role
114
- assert_equal master, @client.current_master
115
- end
116
-
117
- end
118
- end
@@ -1,381 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
-
3
- module Beetle
4
- class RedisConfigurationServerTest < Minitest::Test
5
- def setup
6
- Beetle.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
7
- @server = RedisConfigurationServer.new
8
- EventMachine.stubs(:add_timer).yields
9
- end
10
-
11
- test "should exit when started with less than two redis configured" do
12
- Beetle.config.redis_servers = ""
13
- assert_raises Beetle::ConfigurationError do
14
- @server.start
15
- end
16
- end
17
-
18
- test "should initialize the current token for messages to not reuse old tokens" do
19
- sleep 0.1
20
- later_server = RedisConfigurationServer.new
21
- assert later_server.current_token > @server.current_token
22
- end
23
-
24
- test "should ignore outdated client_invalidated messages" do
25
- @server.instance_variable_set(:@current_token, 2)
26
- @server.client_invalidated("id" => "rc-client-1", "token" => 2)
27
- @server.client_invalidated("id" => "rc-client-2", "token" => 1)
28
-
29
- assert_equal(["rc-client-1"].to_set, @server.instance_variable_get(:@client_invalidated_ids_received))
30
- end
31
-
32
- test "should ignore outdated pong messages" do
33
- @server.instance_variable_set(:@current_token, 2)
34
- @server.pong("id" => "rc-client-1", "token" => 2)
35
- @server.pong("id" => "rc-client-2", "token" => 1)
36
-
37
- assert_equal(["rc-client-1"].to_set, @server.instance_variable_get(:@client_pong_ids_received))
38
- end
39
-
40
- test "the dispatcher should just forward messages to the server" do
41
- dispatcher_class = RedisConfigurationServer.class_eval "MessageDispatcher"
42
- dispatcher_class.configuration_server = @server
43
- dispatcher = dispatcher_class.new
44
- payload = {"token" => 1}
45
- dispatcher.stubs(:message).returns(stub(:data => payload.to_json, :header => stub(:routing_key=> "pong")))
46
- @server.expects(:pong).with(payload)
47
- dispatcher.send(:process)
48
- end
49
-
50
- test "if a new master is available, it should be published and the available slaves should be configured" do
51
- redis = Redis.new
52
- other_master = Redis.new(:port => 6380)
53
- other_master.expects(:slave_of!).with(redis.host, redis.port)
54
- @server.stubs(:current_master).returns(redis)
55
- @server.redis.instance_variable_set(:@server_info, {"master" => [redis, other_master], "slave" => [], "unknown" => []})
56
- payload = @server.send(:payload_with_current_token, {"server" => redis.server})
57
- @server.beetle.expects(:publish).with(:reconfigure, payload)
58
- @server.master_available!
59
- end
60
-
61
- test "should be able to report current status" do
62
- @server.expects(:unknown_client_ids).returns(Set.new ["x", "y"])
63
- @server.expects(:unresponsive_clients).returns(["a", Time.now - 200])
64
- assert @server.status.is_a?(Hash)
65
- end
66
-
67
- test "should be able to calculate unseen clients array" do
68
- assert_equal @server.client_ids.sort, @server.unseen_client_ids
69
- @server.send(:client_seen, "rc-client-1")
70
- assert_equal ["rc-client-2"], @server.unseen_client_ids
71
- @server.send(:client_seen, "rc-client-2")
72
- assert_equal [], @server.unseen_client_ids
73
- end
74
-
75
- test "should not execute a conditional master switch if the current master is available" do
76
- @server.expects(:master_available?).returns(true)
77
- @server.expects(:paused?).returns(false)
78
- @server.expects(:master_unavailable!).never
79
- assert !@server.initiate_master_switch
80
- end
81
-
82
- test "should not execute a conditional master switch if a switch is already in progress" do
83
- @server.expects(:master_available?).returns(false)
84
- @server.expects(:paused?).returns(true)
85
- @server.expects(:master_unavailable!).never
86
- assert @server.initiate_master_switch
87
- end
88
-
89
- test "should execute a conditional master switch if the current master is unavailable and no switch is in progress yet" do
90
- @server.expects(:master_available?).returns(false)
91
- @server.expects(:master_unavailable!).once
92
- @server.expects(:paused?).returns(false)
93
- assert @server.initiate_master_switch
94
- end
95
-
96
- test "should put a limit on the number of stored unknown client ids" do
97
- 1000.times do |i|
98
- id = i.to_s
99
- @server.send(:client_seen, id)
100
- @server.send(:add_unknown_client_id, id)
101
- end
102
- assert @server.unknown_client_ids.size < 100
103
- assert_equal @server.unknown_client_ids.size, @server.clients_last_seen.size
104
- end
105
-
106
- test "should assume clients to be unresponsive after specified interval time" do
107
- @server.send(:client_seen, "1")
108
- @server.send(:client_seen, "2")
109
- @server.client_dead_threshold = 0
110
- assert_equal %w(1 2), @server.unresponsive_clients.map(&:first)
111
- @server.client_dead_threshold = 10
112
- assert_equal [], @server.unresponsive_clients
113
- end
114
- end
115
-
116
- class RedisConfigurationServerInvalidationTest < Minitest::Test
117
- def setup
118
- Beetle.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
119
- Beetle.config.redis_servers = "redis:0,redis:1"
120
- @server = RedisConfigurationServer.new
121
- @server.instance_variable_set(:@current_master, stub('redis stub', :server => 'stubbed_server', :available? => false))
122
- @server.stubs(:verify_redis_master_file_string)
123
- @server.beetle.stubs(:listen).yields
124
- @server.beetle.stubs(:publish)
125
- EM::Timer.stubs(:new).returns(true)
126
- EventMachine.stubs(:add_periodic_timer).yields
127
- end
128
-
129
- test "should pause watching of the redis master when it becomes unavailable" do
130
- @server.expects(:determine_initial_master)
131
- EM.stubs(:add_periodic_timer).returns(stub("timer", :cancel => true))
132
- @server.start
133
- assert !@server.paused?
134
- @server.master_unavailable!
135
- assert @server.paused?
136
- end
137
-
138
- test "should setup an invalidation timeout" do
139
- EM::Timer.expects(:new).yields
140
- @server.expects(:cancel_invalidation)
141
- @server.master_unavailable!
142
- end
143
-
144
- test "should continue watching after the invalidation timeout has expired" do
145
- EM::Timer.expects(:new).yields
146
- @server.master_unavailable!
147
- assert !@server.paused?
148
- end
149
-
150
- test "should invalidate the current master after receiving all pong messages" do
151
- EM::Timer.expects(:new).yields.returns(:timer)
152
- @server.beetle.expects(:publish).with(:invalidate, anything)
153
- @server.expects(:cancel_invalidation)
154
- @server.expects(:redeem_token).with(1).twice.returns(true)
155
- @server.pong("token" => 1, "id" => "rc-client-1")
156
- @server.pong("token" => 1, "id" => "rc-client-2")
157
- end
158
-
159
- test "should switch the current master after receiving all client_invalidated messages" do
160
- @server.expects(:redeem_token).with(1).twice.returns(true)
161
- @server.expects(:switch_master)
162
- @server.client_invalidated("token" => 1, "id" => "rc-client-1")
163
- @server.client_invalidated("token" => 1, "id" => "rc-client-2")
164
- end
165
-
166
- test "should switch the current master immediately if there are no clients" do
167
- @server.instance_variable_set :@client_ids, Set.new
168
- @server.expects(:switch_master)
169
- @server.master_unavailable!
170
- end
171
-
172
- test "switching the master should turn the new master candidate into a master" do
173
- new_master = stub(:master! => nil, :server => "jo:6379")
174
- @server.beetle.expects(:publish).with(:system_notification, anything)
175
- @server.expects(:determine_new_master).returns(new_master)
176
- @server.expects(:write_redis_master_file).with(new_master.server)
177
- @server.send :switch_master
178
- assert_equal new_master, @server.current_master
179
- end
180
-
181
- test "switching the master should resort to the old master if no candidate can be found" do
182
- old_master = @server.current_master
183
- @server.beetle.expects(:publish).with(:system_notification, anything)
184
- @server.expects(:determine_new_master).returns(nil)
185
- @server.send :switch_master
186
- assert_equal old_master, @server.current_master
187
- end
188
-
189
- test "checking the availability of redis servers should publish the available servers as long as the master is available" do
190
- @server.expects(:master_available?).returns(true)
191
- @server.expects(:master_available!)
192
- @server.send(:master_watcher).send(:check_availability)
193
- end
194
-
195
- test "checking the availability of redis servers should call master_unavailable after trying the specified number of times" do
196
- @server.stubs(:master_available?).returns(false)
197
- @server.expects(:master_unavailable!)
198
- watcher = @server.send(:master_watcher)
199
- watcher.instance_variable_set :@master_retries, 0
200
- watcher.send(:check_availability)
201
- end
202
- end
203
-
204
- class RedisConfigurationServerInitialRedisMasterDeterminationTest < Minitest::Test
205
- def setup
206
- EM::Timer.stubs(:new).returns(true)
207
- EventMachine.stubs(:add_periodic_timer).yields
208
- @client = Client.new(Configuration.new)
209
- @client.stubs(:listen).yields
210
- @client.stubs(:publish)
211
- @client.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
212
- @server = RedisConfigurationServer.new
213
- @server.stubs(:beetle).returns(@client)
214
- @server.stubs(:write_redis_master_file)
215
- @redis_master = build_master_redis_stub
216
- @redis_slave = build_slave_redis_stub
217
- @server.instance_variable_set(:@redis, build_redis_server_info(@redis_master, @redis_slave))
218
- end
219
-
220
- test "should not try to auto-detect if the master file contains a server string" do
221
- @server.expects(:master_file_exists?).returns(true)
222
- @server.stubs(:read_redis_master_file).returns("foobar:0000")
223
-
224
- @server.redis.expects(:auto_detect_master).never
225
- @server.expects(:redis_master_from_master_file).returns(@redis_master)
226
- @server.send(:determine_initial_master)
227
- end
228
-
229
- test "should try to auto-detect if the master file is empty" do
230
- @server.expects(:master_file_exists?).returns(true)
231
- @server.stubs(:read_redis_master_file).returns("")
232
-
233
- @server.redis.expects(:auto_detect_master).returns(@redis_master)
234
- @server.send(:determine_initial_master)
235
- end
236
-
237
- test "should try to auto-detect if the master file is not present" do
238
- @server.expects(:master_file_exists?).returns(false)
239
-
240
- @server.redis.expects(:auto_detect_master).returns(@redis_master)
241
- @server.send(:determine_initial_master)
242
- end
243
-
244
- test "should use redis master from successful auto-detection" do
245
- @server.expects(:master_file_exists?).returns(false)
246
-
247
- @server.expects(:write_redis_master_file).with(@redis_master.server)
248
- @server.send(:determine_initial_master)
249
- assert_equal @redis_master, @server.current_master
250
- end
251
-
252
- test "should use redis master if master in file is the only master" do
253
- @server.expects(:master_file_exists?).returns(true)
254
- @server.stubs(:redis_master_from_master_file).returns(@redis_master)
255
-
256
- @server.send(:determine_initial_master)
257
- assert_equal @redis_master, @server.current_master
258
- end
259
-
260
- test "should start master switch if master in file is slave" do
261
- @server.instance_variable_set(:@redis, build_redis_server_info(@redis_slave))
262
- @server.expects(:master_file_exists?).returns(true)
263
- @server.stubs(:redis_master_from_master_file).returns(@redis_slave)
264
-
265
- @server.expects(:master_unavailable!)
266
- @server.send(:determine_initial_master)
267
- end
268
-
269
- test "should use master from master file if multiple masters are available" do
270
- redis_master2 = build_master_redis_stub
271
- @server.instance_variable_set(:@redis, build_redis_server_info(@redis_master, redis_master2))
272
- @server.expects(:master_file_exists?).returns(true)
273
- @server.stubs(:redis_master_from_master_file).returns(@redis_master)
274
-
275
- @server.send(:determine_initial_master)
276
- assert_equal @redis_master, @server.current_master
277
- end
278
-
279
- test "should start master switch if master in file is not available" do
280
- not_available_redis_master = build_unknown_redis_stub
281
- @server.instance_variable_set(:@redis, build_redis_server_info(not_available_redis_master, @redis_slave))
282
- @server.expects(:master_file_exists?).returns(true)
283
- @server.stubs(:redis_master_from_master_file).returns(not_available_redis_master)
284
-
285
- @server.expects(:master_unavailable!)
286
- @server.send(:determine_initial_master)
287
- end
288
-
289
- test "should raise an exception if both master file and auto-detection fails" do
290
- not_available_redis_master = build_unknown_redis_stub
291
- not_available_redis_slave = build_unknown_redis_stub
292
- @server.instance_variable_set(:@redis, build_redis_server_info(not_available_redis_master, not_available_redis_slave))
293
- @server.expects(:master_file_exists?).returns(true)
294
- @server.expects(:read_redis_master_file).returns("")
295
- @server.redis.expects(:auto_detect_master).returns(nil)
296
-
297
- assert_raises Beetle::NoRedisMaster do
298
- @server.send(:determine_initial_master)
299
- end
300
- end
301
-
302
- test "should detect a new redis_master" do
303
- not_available_redis_master = build_unknown_redis_stub
304
- @redis_slave.expects(:slave_of?).returns(true)
305
- @server.instance_variable_set(:@current_master, not_available_redis_master)
306
- @server.instance_variable_set(:@redis, build_redis_server_info(@redis_slave, not_available_redis_master))
307
- assert_equal @redis_slave, @server.send(:determine_new_master)
308
- end
309
-
310
- private
311
-
312
- def build_master_redis_stub
313
- stub("redis master", :host => "stubbed_master", :port => 0, :server => "stubbed_master:0", :available? => true, :master? => true, :slave? => false, :role => "master")
314
- end
315
-
316
- def build_slave_redis_stub
317
- stub("redis slave", :host => "stubbed_slave", :port => 0, :server => "stubbed_slave:0", :available? => true, :master? => false, :slave? => true, :role => "slave")
318
- end
319
-
320
- def build_unknown_redis_stub
321
- stub("redis unknown", :host => "stubbed_unknown", :port => 0, :server => "stubbed_unknown:0", :available? => false, :master? => false, :slave? => false, :role => "unknown")
322
- end
323
-
324
- def build_redis_server_info(*redis_instances)
325
- info = RedisServerInfo.new(Beetle.config, :timeout => 1)
326
- info.instance_variable_set :@instances, redis_instances
327
- redis_instances.each{|redis| info.send("#{redis.role}s") << redis }
328
- info
329
- end
330
- end
331
-
332
- class RedisConfigurationServerSystemNotificationTest < Minitest::Test
333
- def setup
334
- Beetle.config.redis_configuration_client_ids = "rc-client-1,rc-client-2"
335
- @server = RedisConfigurationServer.new
336
- @server.stubs(:beetle).returns(stub(:publish))
337
- EventMachine.stubs(:add_timer).yields
338
- end
339
-
340
- test "should send a system notification when receiving pong message from unknown client" do
341
- payload = {"id" => "unknown-client", "token" => @server.current_token}
342
- msg = "Received pong message from unknown id 'unknown-client'"
343
- @server.beetle.expects(:publish).with(:system_notification, ({:message => msg}).to_json)
344
- @server.pong(payload)
345
- assert @server.unknown_client_ids.include?("unknown-client")
346
- end
347
-
348
- test "should send a system notification when receiving client_started message from unknown client" do
349
- payload = {"id" => "unknown-client"}
350
- msg = "Received client_started message from unknown id 'unknown-client'"
351
- @server.beetle.expects(:publish).with(:system_notification, ({:message => msg}).to_json)
352
- @server.client_started(payload)
353
- assert @server.unknown_client_ids.include?("unknown-client")
354
- end
355
-
356
- test "should not send a system notification when receiving a client started message from a known client" do
357
- payload = {"id" => "rc-client-1"}
358
- @server.beetle.expects(:publish).never
359
- @server.expects(:add_unknown_client_id).never
360
- @server.client_started(payload)
361
- assert @server.clients_last_seen.include?("rc-client-1")
362
- end
363
-
364
- test "should send a system notification when receiving heartbeat message from unknown client" do
365
- payload = {"id" => "unknown-client"}
366
- msg = "Received heartbeat message from unknown id 'unknown-client'"
367
- @server.beetle.expects(:publish).with(:system_notification, ({:message => msg}).to_json)
368
- @server.heartbeat(payload)
369
- assert @server.unknown_client_ids.include?("unknown-client")
370
- end
371
-
372
- test "should not send a system notification when receiving a heartbeat message from a known client" do
373
- payload = {"id" => "rc-client-1"}
374
- @server.beetle.expects(:publish).never
375
- @server.expects(:add_unknown_client_id).never
376
- @server.heartbeat(payload)
377
- assert @server.clients_last_seen.include?("rc-client-1")
378
- end
379
-
380
- end
381
- end