beetle 1.0.3 → 2.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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