nofxx-nanite 0.4.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +430 -0
  3. data/Rakefile +76 -0
  4. data/TODO +24 -0
  5. data/bin/nanite-admin +65 -0
  6. data/bin/nanite-agent +79 -0
  7. data/bin/nanite-mapper +50 -0
  8. data/lib/nanite.rb +74 -0
  9. data/lib/nanite/actor.rb +71 -0
  10. data/lib/nanite/actor_registry.rb +26 -0
  11. data/lib/nanite/admin.rb +138 -0
  12. data/lib/nanite/agent.rb +258 -0
  13. data/lib/nanite/amqp.rb +54 -0
  14. data/lib/nanite/cluster.rb +236 -0
  15. data/lib/nanite/config.rb +111 -0
  16. data/lib/nanite/console.rb +39 -0
  17. data/lib/nanite/daemonize.rb +13 -0
  18. data/lib/nanite/dispatcher.rb +92 -0
  19. data/lib/nanite/identity.rb +16 -0
  20. data/lib/nanite/job.rb +104 -0
  21. data/lib/nanite/local_state.rb +34 -0
  22. data/lib/nanite/log.rb +66 -0
  23. data/lib/nanite/log/formatter.rb +39 -0
  24. data/lib/nanite/mapper.rb +310 -0
  25. data/lib/nanite/mapper_proxy.rb +67 -0
  26. data/lib/nanite/packets.rb +365 -0
  27. data/lib/nanite/pid_file.rb +52 -0
  28. data/lib/nanite/reaper.rb +38 -0
  29. data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
  30. data/lib/nanite/security/certificate.rb +55 -0
  31. data/lib/nanite/security/certificate_cache.rb +66 -0
  32. data/lib/nanite/security/distinguished_name.rb +34 -0
  33. data/lib/nanite/security/encrypted_document.rb +46 -0
  34. data/lib/nanite/security/rsa_key_pair.rb +53 -0
  35. data/lib/nanite/security/secure_serializer.rb +68 -0
  36. data/lib/nanite/security/signature.rb +46 -0
  37. data/lib/nanite/security/static_certificate_store.rb +35 -0
  38. data/lib/nanite/security_provider.rb +47 -0
  39. data/lib/nanite/serializer.rb +52 -0
  40. data/lib/nanite/state.rb +164 -0
  41. data/lib/nanite/streaming.rb +125 -0
  42. data/lib/nanite/util.rb +58 -0
  43. data/spec/actor_registry_spec.rb +60 -0
  44. data/spec/actor_spec.rb +77 -0
  45. data/spec/agent_spec.rb +240 -0
  46. data/spec/cached_certificate_store_proxy_spec.rb +34 -0
  47. data/spec/certificate_cache_spec.rb +49 -0
  48. data/spec/certificate_spec.rb +27 -0
  49. data/spec/cluster_spec.rb +485 -0
  50. data/spec/dispatcher_spec.rb +136 -0
  51. data/spec/distinguished_name_spec.rb +24 -0
  52. data/spec/encrypted_document_spec.rb +21 -0
  53. data/spec/job_spec.rb +251 -0
  54. data/spec/local_state_spec.rb +112 -0
  55. data/spec/packet_spec.rb +220 -0
  56. data/spec/rsa_key_pair_spec.rb +33 -0
  57. data/spec/secure_serializer_spec.rb +41 -0
  58. data/spec/serializer_spec.rb +107 -0
  59. data/spec/signature_spec.rb +30 -0
  60. data/spec/spec_helper.rb +33 -0
  61. data/spec/static_certificate_store_spec.rb +30 -0
  62. data/spec/util_spec.rb +63 -0
  63. metadata +131 -0
@@ -0,0 +1,27 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Certificate do
4
+
5
+ include SpecHelpers
6
+
7
+ before(:all) do
8
+ @certificate, key = issue_cert
9
+ end
10
+
11
+ it 'should save' do
12
+ filename = File.join(File.dirname(__FILE__), "cert.pem")
13
+ @certificate.save(filename)
14
+ File.size(filename).should be > 0
15
+ File.delete(filename)
16
+ end
17
+
18
+ it 'should load' do
19
+ filename = File.join(File.dirname(__FILE__), "cert.pem")
20
+ @certificate.save(filename)
21
+ cert = Nanite::Certificate.load(filename)
22
+ File.delete(filename)
23
+ cert.should_not be_nil
24
+ cert.data.should == @certificate.data
25
+ end
26
+
27
+ end
@@ -0,0 +1,485 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Nanite::Cluster do
4
+
5
+ include SpecHelpers
6
+
7
+ describe "Intialization" do
8
+
9
+ before(:each) do
10
+ @fanout = mock("fanout")
11
+ @binding = mock("binding", :subscribe => true)
12
+ @queue = mock("queue", :bind => @binding)
13
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
14
+ @serializer = mock("Serializer")
15
+ @reaper = mock("Reaper")
16
+ @mapper = mock("Mapper")
17
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
18
+ end
19
+
20
+ describe "of Heartbeat (Queue)" do
21
+
22
+ it "should setup the heartbeat (queue) for id" do
23
+ @amq.should_receive(:queue).with("heartbeat-the_identity", anything()).and_return(@queue)
24
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
25
+ end
26
+
27
+ it "should make the heartbeat (queue) exclusive" do
28
+ @amq.should_receive(:queue).with("heartbeat-the_identity", { :exclusive => true }).and_return(@queue)
29
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
30
+ end
31
+
32
+ it "should bind the heartbeat (queue) to 'heartbeat' fanout" do
33
+ @amq.should_receive(:fanout).with("heartbeat", { :durable => true }).and_return(@fanout)
34
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
35
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
36
+ end
37
+
38
+ end # of Heartbeat (Queue)
39
+
40
+
41
+ describe "of Registration (Queue)" do
42
+
43
+ it "should setup the registration (queue) for id" do
44
+ @amq.should_receive(:queue).with("registration-the_identity", anything()).and_return(@queue)
45
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
46
+ end
47
+
48
+ it "should make the registration (queue) exclusive" do
49
+ @amq.should_receive(:queue).with("registration-the_identity", { :exclusive => true }).and_return(@queue)
50
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
51
+ end
52
+
53
+ it "should bind the registration (queue) to 'registration' fanout" do
54
+ @amq.should_receive(:fanout).with("registration", { :durable => true }).and_return(@fanout)
55
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
56
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
57
+ end
58
+
59
+ end # of Registration (Queue)
60
+
61
+ describe "of Request (Queue)" do
62
+
63
+ it "should setup the request (queue) for id" do
64
+ @amq.should_receive(:queue).with("request-the_identity", anything()).and_return(@queue)
65
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
66
+ end
67
+
68
+ it "should make the request (queue) exclusive" do
69
+ @amq.should_receive(:queue).with("request-the_identity", { :exclusive => true }).and_return(@queue)
70
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
71
+ end
72
+
73
+ it "should bind the request (queue) to 'request' fanout" do
74
+ @amq.should_receive(:fanout).with("request", { :durable => true }).and_return(@fanout)
75
+ @queue.should_receive(:bind).with(@fanout).and_return(@binding)
76
+ cluster = Nanite::Cluster.new(@amq, 10, "the_identity", @serializer, @mapper)
77
+ end
78
+
79
+ end # of Request (Queue)
80
+
81
+
82
+ describe "Reaper" do
83
+
84
+ it "should be created" do
85
+ Nanite::Reaper.should_receive(:new).with(anything()).and_return(@reaper)
86
+ cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
87
+ end
88
+
89
+ it "should use the agent timeout" do
90
+ Nanite::Reaper.should_receive(:new).with(443).and_return(@reaper)
91
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
92
+ end
93
+
94
+ end # Reaper
95
+
96
+ describe "State" do
97
+ begin
98
+ require 'nanite/state'
99
+ rescue LoadError
100
+ end
101
+
102
+ if defined?(Redis)
103
+ it "should use a local state by default" do
104
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper)
105
+ cluster.nanites.instance_of?(Nanite::LocalState).should == true
106
+ end
107
+
108
+ it "should set up a redis state when requested" do
109
+ state = Nanite::State.new("")
110
+ Nanite::State.should_receive(:new).with("localhost:1234").and_return(state)
111
+ cluster = Nanite::Cluster.new(@amq, 443, "the_identity", @serializer, @mapper, "localhost:1234")
112
+ cluster.nanites.instance_of?(Nanite::State).should == true
113
+ end
114
+ end
115
+ end
116
+ end # Intialization
117
+
118
+
119
+ describe "Target Selection" do
120
+
121
+ before(:each) do
122
+ @fanout = mock("fanout")
123
+ @binding = mock("binding", :subscribe => true)
124
+ @queue = mock("queue", :bind => @binding)
125
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
126
+ @serializer = mock("Serializer")
127
+ @reaper = mock("Reaper")
128
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
129
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
130
+ end
131
+
132
+ it "should return array containing targets for request" do
133
+ target = mock("Supplied Target")
134
+ request = mock("Request", :target => target)
135
+ @cluster.targets_for(request).should be_instance_of(Array)
136
+ end
137
+
138
+ it "should use target from request" do
139
+ target = mock("Supplied Target")
140
+ request = mock("Request", :target => target)
141
+ @cluster.targets_for(request).should == [target]
142
+ end
143
+
144
+ it "should use targets choosen by least loaded selector (:least_loaded)" do
145
+ targets = { "target 3" => 3 }
146
+ request = mock("Request", :target => nil, :selector => :least_loaded, :type => "service", :tags => [])
147
+ @cluster.should_receive(:least_loaded).with("service", []).and_return(targets)
148
+ @cluster.targets_for(request).should == ["target 3"]
149
+ end
150
+
151
+ it "should use targets choosen by all selector (:all)" do
152
+ targets = { "target 1" => 1, "target 2" => 2, "target 3" => 3 }
153
+ request = mock("Request", :target => nil, :selector => :all, :type => "service", :tags => [])
154
+ @cluster.should_receive(:all).with("service", []).and_return(targets)
155
+ @cluster.targets_for(request).should == ["target 1", "target 2", "target 3"]
156
+ end
157
+
158
+ it "should use targets choosen by random selector (:random)" do
159
+ targets = { "target 3" => 3 }
160
+ request = mock("Request", :target => nil, :selector => :random, :type => "service", :tags => [])
161
+ @cluster.should_receive(:random).with("service", []).and_return(targets)
162
+ @cluster.targets_for(request).should == ["target 3"]
163
+ end
164
+
165
+ it "should use targets choosen by round-robin selector (:rr)" do
166
+ targets = { "target 2" => 2 }
167
+ request = mock("Request", :target => nil, :selector => :rr, :type => "service", :tags => [])
168
+ @cluster.should_receive(:rr).with("service", []).and_return(targets)
169
+ @cluster.targets_for(request).should == ["target 2"]
170
+ end
171
+
172
+ end # Target Selection
173
+
174
+
175
+ describe "Nanite Registration" do
176
+
177
+ before(:each) do
178
+ @fanout = mock("fanout")
179
+ @binding = mock("binding", :subscribe => true)
180
+ @queue = mock("queue", :bind => @binding)
181
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
182
+ @serializer = mock("Serializer")
183
+ @reaper = mock("Reaper", :timeout => true)
184
+ Nanite::Log.stub!(:info)
185
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
186
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
187
+ @register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
188
+ end
189
+
190
+ it "should add the Nanite to the nanites map" do
191
+ @cluster.register(@register_packet)
192
+ @cluster.nanites['nanite_id'].should_not be_nil
193
+ end
194
+
195
+ it "should use hash of the Nanite's services and status as value" do
196
+ @cluster.register(@register_packet)
197
+ @cluster.nanites['nanite_id'].keys.size == 2
198
+ @cluster.nanites['nanite_id'].keys.should include(:services)
199
+ @cluster.nanites['nanite_id'].keys.should include(:status)
200
+ @cluster.nanites['nanite_id'][:services].should == ["the_nanite_services"]
201
+ @cluster.nanites['nanite_id'][:status].should == "nanite_status"
202
+ end
203
+
204
+ it "should add nanite to reaper" do
205
+ @reaper.should_receive(:timeout).with('nanite_id', 33)
206
+ @cluster.register(@register_packet)
207
+ end
208
+
209
+ it "should log info message that nanite was registered" do
210
+ Nanite::Log.should_receive(:info)
211
+ @cluster.register(@register_packet)
212
+ end
213
+
214
+ describe "with registered callbacks" do
215
+ before(:each) do
216
+ @register_callback = lambda {|request, mapper|}
217
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :register => @register_callback)
218
+ end
219
+
220
+ it "should call the registration callback" do
221
+ @register_callback.should_receive(:call).with("nanite_id", @mapper)
222
+ @cluster.register(@register_packet)
223
+ end
224
+ end
225
+
226
+ describe "when sending an invalid packet to the registration queue" do
227
+ it "should log a message statement" do
228
+ Nanite::Log.logger.should_receive(:warn).with("RECV [register] Invalid packet type: Nanite::Ping")
229
+ @cluster.register(Nanite::Ping.new(nil, nil))
230
+ end
231
+ end
232
+ end # Nanite Registration
233
+
234
+ describe "Unregister" do
235
+ before(:each) do
236
+ @fanout = mock("fanout")
237
+ @binding = mock("binding", :subscribe => true)
238
+ @queue = mock("queue", :bind => @binding)
239
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
240
+ @serializer = mock("Serializer")
241
+ @reaper = mock("Reaper", :timeout => true)
242
+ Nanite::Log.stub!(:info)
243
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
244
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
245
+ @cluster.nanites["nanite_id"] = "nanite_id"
246
+ @unregister_packet = Nanite::UnRegister.new("nanite_id")
247
+ end
248
+
249
+ it "should delete the nanite" do
250
+ @cluster.register(@unregister_packet)
251
+ @cluster.nanites["nanite_id"].should == nil
252
+ end
253
+
254
+ describe "with registered callbacks" do
255
+ before(:each) do
256
+ @unregister_callback = lambda {|request, mapper| }
257
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper, nil, :unregister => @unregister_callback)
258
+ @cluster.nanites["nanite_id"] = "nanite_id"
259
+ end
260
+
261
+ it "should call the unregister callback" do
262
+ @unregister_callback.should_receive(:call).with("nanite_id", @mapper)
263
+ @cluster.register(@unregister_packet)
264
+ end
265
+ end
266
+ end
267
+
268
+ describe "Nanite timed out" do
269
+ before(:each) do
270
+ @fanout = mock("fanout")
271
+ @binding = mock("binding", :subscribe => true)
272
+ @queue = mock("queue", :bind => @binding)
273
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
274
+ @serializer = mock("Serializer")
275
+ Nanite::Log.stub!(:info)
276
+ @register_packet = Nanite::Register.new("nanite_id", ["the_nanite_services"], "nanite_status",[])
277
+ end
278
+
279
+ it "should remove the nanite when timed out" do
280
+ EM.run do
281
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
282
+ @cluster.register(@register_packet)
283
+ EM.add_timer(1.1) {
284
+ @cluster.nanites["nanite_id"].should == nil
285
+ EM.stop_event_loop
286
+ }
287
+ end
288
+ end
289
+
290
+ it "should call the timed out callback handler when registered" do
291
+ EM.run do
292
+ @cluster = Nanite::Cluster.new(@amq, 0.01, "the_identity", @serializer, @mapper)
293
+ @cluster.register(@register_packet)
294
+ EM.add_timer(1.1) {
295
+ @cluster.nanites["nanite_id"].should == nil
296
+ EM.stop_event_loop
297
+ }
298
+ end
299
+ end
300
+ end
301
+
302
+ describe "Route" do
303
+
304
+ before(:each) do
305
+ @fanout = mock("fanout")
306
+ @binding = mock("binding", :subscribe => true)
307
+ @queue = mock("queue", :bind => @binding)
308
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
309
+ @serializer = mock("Serializer")
310
+ @reaper = mock("Reaper")
311
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
312
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
313
+ @request = mock("Request")
314
+ end
315
+
316
+ it "should publish request to all targets" do
317
+ target1 = mock("Target 1")
318
+ target2 = mock("Target 2")
319
+ @cluster.should_receive(:publish).with(@request, target1)
320
+ @cluster.should_receive(:publish).with(@request, target2)
321
+ EM.run {
322
+ @cluster.route(@request, [target1, target2])
323
+ EM.stop
324
+ }
325
+ end
326
+
327
+ end # Route
328
+
329
+
330
+ describe "Publish" do
331
+
332
+ before(:each) do
333
+ @fanout = mock("fanout")
334
+ @binding = mock("binding", :subscribe => true)
335
+ @queue = mock("queue", :bind => @binding, :publish => true)
336
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
337
+ @serializer = mock("Serializer", :dump => "dumped_value")
338
+ @reaper = mock("Reaper")
339
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
340
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
341
+ @request = mock("Request", :persistent => true, :target => nil, :target= => nil, :to_s => nil)
342
+ @target = mock("Target of Request")
343
+ end
344
+
345
+ it "should serialize request before publishing it" do
346
+ @request.should_receive(:target=).with(@target)
347
+ @request.should_receive(:target=)
348
+ @request.should_receive(:target)
349
+ @serializer.should_receive(:dump).with(@request).and_return("serialized_request")
350
+ @cluster.publish(@request, @target)
351
+ end
352
+
353
+ it "should publish request to target queue" do
354
+ @request.should_receive(:target=).with(@target)
355
+ @request.should_receive(:target=)
356
+ @request.should_receive(:target)
357
+ @queue.should_receive(:publish).with("dumped_value", anything())
358
+ @cluster.publish(@request, @target)
359
+ end
360
+
361
+ it "should persist request based on request setting" do
362
+ @request.should_receive(:target=).with(@target)
363
+ @request.should_receive(:target=)
364
+ @request.should_receive(:target)
365
+ @request.should_receive(:persistent).and_return(false)
366
+ @queue.should_receive(:publish).with(anything(), { :persistent => false })
367
+ @cluster.publish(@request, @target)
368
+ end
369
+
370
+ end # Publish
371
+
372
+ describe "Agent Request Handling" do
373
+
374
+ before(:each) do
375
+ @fanout = mock("fanout")
376
+ @binding = mock("binding", :subscribe => true)
377
+ @queue = mock("queue", :bind => @binding, :publish => true)
378
+ @amq = mock("AMPQueue", :queue => @queue, :fanout => @fanout)
379
+ @serializer = mock("Serializer", :dump => "dumped_value")
380
+ @target = mock("Target of Request")
381
+ @reaper = mock("Reaper")
382
+ Nanite::Reaper.stub!(:new).and_return(@reaper)
383
+ @request_without_target = mock("Request", :target => nil, :token => "Token",
384
+ :reply_to => "Reply To", :from => "From", :persistent => true, :identity => "Identity",
385
+ :payload => "Payload", :to_s => nil)
386
+ @request_with_target = mock("Request", :target => "Target", :token => "Token",
387
+ :reply_to => "Reply To", :from => "From", :persistent => true, :payload => "Payload", :to_s => nil)
388
+ @mapper_with_target = mock("Mapper", :identity => "id")
389
+ @mapper_without_target = mock("Mapper", :request => false, :identity => @request_without_target.identity)
390
+ @cluster_with_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_with_target)
391
+ @cluster_without_target = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper_without_target)
392
+ Nanite::Cluster.stub!(:mapper).and_return(@mapper)
393
+ end
394
+
395
+ it "should forward requests with targets" do
396
+ @mapper_with_target.should_receive(:send_request).with(@request_with_target, anything())
397
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
398
+ end
399
+
400
+ it "should reply back with nil results for requests with no target when offline queue is disabled" do
401
+ @mapper_without_target.should_receive(:send_request).with(@request_without_target, anything())
402
+ Nanite::Result.should_receive(:new).with(@request_without_target.token, @request_without_target.from, nil, @request_without_target.identity)
403
+ @cluster_without_target.__send__(:handle_request, @request_without_target)
404
+ end
405
+
406
+ it "should hand in an intermediate handler" do
407
+ @mapper_with_target.should_receive(:send_request) do |request, opts|
408
+ opts[:intermediate_handler].should be_instance_of(Proc)
409
+ end
410
+
411
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
412
+ end
413
+
414
+ it "should forward the message when send_request failed" do
415
+ @mapper_with_target.stub!(:send_request).and_return(false)
416
+ @cluster_with_target.should_receive(:forward_response)
417
+ @cluster_with_target.__send__(:handle_request, @request_with_target)
418
+ end
419
+
420
+ describe "when getting push requests from an agent" do
421
+ it "should send the push message through the mapper" do
422
+ push = Nanite::Push.new(nil, nil)
423
+ @mapper_with_target.should_receive(:send_push).with(push)
424
+ @cluster_with_target.__send__(:handle_request, push)
425
+ end
426
+ end
427
+ end # Agent Request Handling
428
+
429
+ describe "Heartbeat" do
430
+ before(:each) do
431
+ @fanout = mock("fanout")
432
+ @binding = mock("binding", :subscribe => true)
433
+ @queue = mock("queue", :bind => @binding, :publish => true)
434
+ @amq = mock("AMQueue", :queue => @queue, :fanout => @fanout)
435
+ @serializer = mock("Serializer", :dump => "dumped_value")
436
+ Nanite::Log.stub!(:info)
437
+ @ping = stub("ping", :status => 0.3, :identity => "nanite_id")
438
+ end
439
+
440
+ it "should update the nanite status" do
441
+ run_in_em do
442
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
443
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
444
+ @cluster.send :handle_ping, @ping
445
+ @cluster.nanites["nanite_id"][:status].should == 0.3
446
+ end
447
+ end
448
+
449
+ it "should reset the agent time out" do
450
+ run_in_em do
451
+ @cluster = Nanite::Cluster.new(@amq, 32, "the_identity", @serializer, @mapper)
452
+ @cluster.reaper.should_receive(:reset_with_autoregister_hack).with("nanite_id", 33)
453
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
454
+ @cluster.send :handle_ping, @ping
455
+ end
456
+ end
457
+
458
+ describe "when timing out after a heartbeat" do
459
+ it "should remove the nanite" do
460
+ run_in_em(false) do
461
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper)
462
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
463
+ @cluster.send :handle_ping, @ping
464
+ EM.add_timer(1.5) do
465
+ @cluster.nanites["nanite_id"].should == nil
466
+ EM.stop_event_loop
467
+ end
468
+ end
469
+ end
470
+
471
+ it "should call the timeout callback when defined" do
472
+ run_in_em(false) do
473
+ @timeout_callback = lambda {|nanite, mapper| }
474
+ @timeout_callback.should_receive(:call).with("nanite_id", @mapper)
475
+ @cluster = Nanite::Cluster.new(@amq, 0.1, "the_identity", @serializer, @mapper, nil, :timeout => @timeout_callback)
476
+ @cluster.nanites["nanite_id"] = {:status => "nanite_status"}
477
+ @cluster.send :handle_ping, @ping
478
+ EM.add_timer(1.5) do
479
+ EM.stop_event_loop
480
+ end
481
+ end
482
+ end
483
+ end
484
+ end
485
+ end # Nanite::Cluster