nofxx-nanite 0.4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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