rjr 0.18.2 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +16 -9
  4. data/bin/rjr-server +2 -1
  5. data/examples/client.rb +21 -19
  6. data/examples/server.rb +1 -1
  7. data/examples/structured_server.rb +1 -0
  8. data/examples/tcp.rb +1 -0
  9. data/lib/rjr/common.rb +1 -226
  10. data/lib/rjr/core_ext.rb +63 -0
  11. data/lib/rjr/dispatcher.rb +75 -219
  12. data/lib/rjr/messages.rb +8 -0
  13. data/lib/rjr/messages/compressed.rb +264 -0
  14. data/lib/rjr/messages/notification.rb +95 -0
  15. data/lib/rjr/messages/request.rb +99 -0
  16. data/lib/rjr/messages/response.rb +128 -0
  17. data/lib/rjr/node.rb +100 -97
  18. data/lib/rjr/node_callback.rb +43 -0
  19. data/lib/rjr/nodes/amqp.rb +12 -11
  20. data/lib/rjr/nodes/easy.rb +4 -4
  21. data/lib/rjr/nodes/local.rb +13 -12
  22. data/lib/rjr/nodes/multi.rb +1 -1
  23. data/lib/rjr/nodes/tcp.rb +15 -13
  24. data/lib/rjr/nodes/template.rb +4 -4
  25. data/lib/rjr/nodes/unix.rb +15 -13
  26. data/lib/rjr/nodes/web.rb +15 -14
  27. data/lib/rjr/nodes/ws.rb +12 -11
  28. data/lib/rjr/request.rb +128 -0
  29. data/lib/rjr/result.rb +75 -0
  30. data/lib/rjr/util/args.rb +145 -0
  31. data/lib/rjr/{em_adapter.rb → util/em_adapter.rb} +0 -0
  32. data/lib/rjr/util/handles_methods.rb +115 -0
  33. data/lib/rjr/util/has_messages.rb +50 -0
  34. data/lib/rjr/{inspect.rb → util/inspect.rb} +1 -1
  35. data/lib/rjr/util/json_parser.rb +101 -0
  36. data/lib/rjr/util/logger.rb +128 -0
  37. data/lib/rjr/{thread_pool.rb → util/thread_pool.rb} +2 -0
  38. data/lib/rjr/version.rb +1 -1
  39. data/site/jrw.js +1 -1
  40. data/specs/args_spec.rb +144 -0
  41. data/specs/dispatcher_spec.rb +399 -211
  42. data/specs/em_adapter_spec.rb +31 -18
  43. data/specs/handles_methods_spec.rb +154 -0
  44. data/specs/has_messages_spec.rb +54 -0
  45. data/specs/inspect_spec.rb +1 -1
  46. data/specs/json_parser_spec.rb +169 -0
  47. data/specs/messages/notification_spec.rb +59 -0
  48. data/specs/messages/request_spec.rb +66 -0
  49. data/specs/messages/response_spec.rb +94 -0
  50. data/specs/node_callbacks_spec.rb +47 -0
  51. data/specs/node_spec.rb +465 -56
  52. data/specs/request_spec.rb +147 -0
  53. data/specs/result_spec.rb +144 -0
  54. data/specs/thread_pool_spec.rb +1 -1
  55. metadata +41 -11
  56. data/lib/rjr/errors.rb +0 -23
  57. data/lib/rjr/message.rb +0 -351
  58. data/lib/rjr/semaphore.rb +0 -58
  59. data/specs/message_spec.rb +0 -229
@@ -0,0 +1,59 @@
1
+ require 'rjr/messages/notification'
2
+
3
+ module RJR
4
+ module Messages
5
+ describe Notification do
6
+ it "should accept notification parameters" do
7
+ msg = Notification.new :method => 'test',
8
+ :args => ['a', 1],
9
+ :headers => {:h => 2}
10
+ msg.jr_method.should == "test"
11
+ msg.jr_args.should =~ ['a', 1]
12
+ msg.headers.should have_key(:h)
13
+ msg.headers[:h].should == 2
14
+ end
15
+
16
+ it "should return bool indicating if string is a notification msg"
17
+
18
+ it "should produce valid json" do
19
+ msg = Notification.new :method => 'test',
20
+ :args => ['a', 1],
21
+ :headers => {:h => 2}
22
+
23
+ msg_string = msg.to_s
24
+ msg_string.should include('"h":2')
25
+ msg_string.should include('"method":"test"')
26
+ msg_string.should include('"params":["a",1]')
27
+ msg_string.should include('"jsonrpc":"2.0"')
28
+ msg_string.should_not include('"id":"')
29
+ end
30
+
31
+ it "should parse notification message string" do
32
+ msg_string = '{"jsonrpc": "2.0", ' +
33
+ '"method": "test", "params": ["a", 1]}'
34
+ msg = Notification.new :message => msg_string
35
+ msg.json_message.should == msg_string
36
+ msg.jr_method.should == 'test'
37
+ msg.jr_args.should =~ ['a', 1]
38
+ end
39
+
40
+ it "should extract optional headers from message string" do
41
+ msg_string = '{"jsonrpc": "2.0", ' +
42
+ '"method": "test", "params": ["a", 1], ' +
43
+ '"h": 2}'
44
+ msg = Notification.new :message => msg_string, :headers => {'f' => 'g'}
45
+ msg.json_message.should == msg_string
46
+ msg.headers.should have_key 'h'
47
+ msg.headers.should have_key 'f'
48
+ msg.headers['h'].should == 2
49
+ msg.headers['f'].should == 'g'
50
+ end
51
+
52
+ it "should fail if parsing invalid message string" do
53
+ lambda {
54
+ msg = Notification.new :message => 'foobar'
55
+ }.should raise_error JSON::ParserError
56
+ end
57
+ end # describe Notification
58
+ end # module Messages
59
+ end # module RJR
@@ -0,0 +1,66 @@
1
+ require 'rjr/common'
2
+ require 'rjr/messages/request'
3
+
4
+ module RJR
5
+ module Messages
6
+ describe Request do
7
+ it "should accept request parameters" do
8
+ msg = Request.new :method => 'test',
9
+ :args => ['a', 1],
10
+ :headers => {:h => 2}
11
+ msg.jr_method.should == "test"
12
+ msg.jr_args.should =~ ['a', 1]
13
+ msg.headers.should have_key(:h)
14
+ msg.headers[:h].should == 2
15
+ msg.msg_id.should =~ /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/
16
+ end
17
+
18
+ it "should return bool indicating if string is a request msg"
19
+
20
+ it "should produce valid json" do
21
+ msg = Request.new :method => 'test',
22
+ :args => ['a', 1],
23
+ :headers => {:h => 2}
24
+
25
+ msg_string = msg.to_s
26
+ msg_string.should include('"h":2')
27
+ msg_string.should include('"method":"test"')
28
+ msg_string.should include('"params":["a",1]')
29
+ msg_string.should include('"jsonrpc":"2.0"')
30
+ msg_string.should include('"id":"'+msg.msg_id+'"')
31
+ end
32
+
33
+ it "should parse request message string" do
34
+ msg_uuid = gen_uuid
35
+ msg_string = '{"jsonrpc": "2.0", ' +
36
+ '"id": "' + msg_uuid + '", ' +
37
+ '"method": "test", "params": ["a", 1]}'
38
+ msg = Request.new :message => msg_string
39
+ msg.json_message.should == msg_string
40
+ msg.jr_method.should == 'test'
41
+ msg.jr_args.should =~ ['a', 1]
42
+ msg.msg_id.should == msg_uuid
43
+ end
44
+
45
+ it "should extract optional headers from message string" do
46
+ msg_uuid = gen_uuid
47
+ msg_string = '{"jsonrpc": "2.0", ' +
48
+ '"id": "' + msg_uuid + '", ' +
49
+ '"method": "test", "params": ["a", 1], ' +
50
+ '"h": 2}'
51
+ msg = Request.new :message => msg_string, :headers => {'f' => 'g'}
52
+ msg.json_message.should == msg_string
53
+ msg.headers.should have_key 'h'
54
+ msg.headers.should have_key 'f'
55
+ msg.headers['h'].should == 2
56
+ msg.headers['f'].should == 'g'
57
+ end
58
+
59
+ it "should fail if parsing invalid message string" do
60
+ lambda {
61
+ msg = Request.new :message => 'foobar'
62
+ }.should raise_error JSON::ParserError
63
+ end
64
+ end # describe Request
65
+ end # module Messages
66
+ end # module RJR
@@ -0,0 +1,94 @@
1
+ require 'rjr/common'
2
+ require 'rjr/result'
3
+ require 'rjr/messages/response'
4
+
5
+ module RJR
6
+ module Messages
7
+ describe Response do
8
+ it "should accept response parameters" do
9
+ msg_id = gen_uuid
10
+ msg = Response.new :id => msg_id,
11
+ :result => Result.new(:result => 'success'),
12
+ :headers => {:h => 2}
13
+ msg.msg_id.should == msg_id
14
+ msg.result.result == 'success'
15
+ msg.headers.should have_key(:h)
16
+ msg.headers[:h].should == 2
17
+ end
18
+
19
+ it "should return bool indicating if string is a response msg"
20
+
21
+ it "should produce valid result response json" do
22
+ msg_id = gen_uuid
23
+ msg = Response.new :id => msg_id,
24
+ :result => RJR::Result.new(:result => 'success'),
25
+ :headers => {:h => 2}
26
+ msg_string = msg.to_s
27
+ msg_string.should include('"id":"'+msg_id+'"')
28
+ msg_string.should include('"result":"success"')
29
+ msg_string.should include('"h":2')
30
+ msg_string.should include('"jsonrpc":"2.0"')
31
+ end
32
+
33
+ it "should produce valid error response json" do
34
+ msg_id = gen_uuid
35
+ msg = Response.new :id => msg_id,
36
+ :result =>
37
+ RJR::Result.new(:error_code => 404,
38
+ :error_msg => 'not found',
39
+ :error_class => ArgumentError),
40
+ :headers => {:h => 2}
41
+ msg_string = msg.to_s
42
+ msg_string.should include('"id":"'+msg_id+'"')
43
+ msg_string.should include('"h":2')
44
+ msg_string.should include('"jsonrpc":"2.0"')
45
+ msg_string.should include('"error":{')
46
+ msg_string.should include('"code":404')
47
+ msg_string.should include('"message":"not found"')
48
+ end
49
+
50
+
51
+ it "should parse result response message string" do
52
+ msg_id = gen_uuid
53
+ msg_string = '{"id":"' + msg_id + '", ' +
54
+ '"result":"success","jsonrpc":"2.0"}'
55
+ msg = Response.new :message => msg_string
56
+ msg.json_message.should == msg_string
57
+ msg.msg_id.should == msg_id
58
+ msg.result.success.should == true
59
+ msg.result.failed.should == false
60
+ msg.result.result.should == "success"
61
+ end
62
+
63
+ it "should parse error response message string" do
64
+ msg_id = gen_uuid
65
+ msg_string = '{"id":"' + msg_id + '", ' +
66
+ '"error":{"code":404,"message":"not found","class":"ArgumentError"}, "jsonrpc":"2.0"}'
67
+ msg = Response.new :message => msg_string
68
+ msg.json_message.should == msg_string
69
+ msg.msg_id.should == msg_id
70
+ msg.result.success.should == false
71
+ msg.result.failed.should == true
72
+ msg.result.error_code.should == 404
73
+ msg.result.error_msg.should == "not found"
74
+ msg.result.error_class.should == 'ArgumentError'
75
+ end
76
+
77
+ it "should extract optional headers from message string" do
78
+ msg_id = gen_uuid
79
+ msg_string = '{"id":"' + msg_id + '", ' +
80
+ '"result":"success","h":2,"jsonrpc":"2.0"}'
81
+ msg = Response.new :message => msg_string
82
+ msg.json_message.should == msg_string
83
+ msg.headers.should have_key 'h'
84
+ msg.headers['h'].should == 2
85
+ end
86
+
87
+ it "should fail if parsing invalid message string" do
88
+ lambda {
89
+ msg = Response.new :message => 'foobar'
90
+ }.should raise_error JSON::ParserError
91
+ end
92
+ end # describe Response
93
+ end # module Messages
94
+ end # module RJR
@@ -0,0 +1,47 @@
1
+ require 'rjr/dispatcher'
2
+
3
+ require 'rjr/node'
4
+ require 'rjr/node_callback'
5
+
6
+ module RJR
7
+ describe NodeCallback do
8
+ describe "#initialize" do
9
+ it "sets node" do
10
+ node = Object.new
11
+ callback = NodeCallback.new :node => node
12
+ callback.node.should == node
13
+ end
14
+
15
+ it "sets connection" do
16
+ connection = Object.new
17
+ callback = NodeCallback.new :connection => connection
18
+ callback.connection.should == connection
19
+ end
20
+ end
21
+
22
+ describe "#notify" do
23
+ before(:each) do
24
+ @connection = Object.new
25
+ @node = Node.new :headers => {'msg' => 'headers'}
26
+ @callback = NodeCallback.new :node => @node,
27
+ :connection => @connection
28
+ end
29
+
30
+ context "node is not persistent" do
31
+ it "just returns / does not send msg" do
32
+ @node.should_receive(:persistent?).and_return(false)
33
+ @node.should_not_receive(:send_msg)
34
+ @callback.notify('')
35
+ end
36
+ end
37
+
38
+ it "sends notification via node" do
39
+ expected = Messages::Notification.new :method => 'method1',
40
+ :args => ['method', 'args'],
41
+ :headers => {'msg' => 'headers'}
42
+ @node.should_not_receive(:send_msg).with(expected.to_s, @connection)
43
+ @callback.notify('msg')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -3,13 +3,6 @@ require 'rjr/node'
3
3
 
4
4
  module RJR
5
5
  describe Node do
6
- before(:all) do
7
- # was made public in ruby 1.9
8
- if RUBY_VERSION < "1.9"
9
- RJR::Node.public_class_method(:class_variable_get)
10
- end
11
- end
12
-
13
6
  describe "::persistent?" do
14
7
  context "PERSISTENT_NODE is defined and true" do
15
8
  it "returns true" do
@@ -19,11 +12,19 @@ module RJR
19
12
  end
20
13
  end
21
14
 
22
- context "PERSISTENT_NODE is not defined or returns false" do
15
+ context "PERSISTENT_NODE is not defined" do
23
16
  it "returns false" do
24
17
  Node.should_not be_persistent
25
18
  end
26
19
  end
20
+
21
+ context "PERSISTENT_NODE returns false" do
22
+ it "returns false" do
23
+ new_node = Class.new(Node)
24
+ new_node.const_set(:PERSISTENT_NODE, false)
25
+ new_node.should_not be_persistent
26
+ end
27
+ end
27
28
  end
28
29
 
29
30
  describe "#persistent?" do
@@ -42,69 +43,477 @@ module RJR
42
43
  end
43
44
  end
44
45
 
45
- it "should initialize properly from params" do
46
- d = Dispatcher.new
47
- node = Node.new :node_id => 'foobar',
48
- :headers => {:h => 123},
49
- :dispatcher => d
50
- node.node_id.should == 'foobar'
51
- node.message_headers[:h].should == 123
52
- node.dispatcher.should == d
46
+ describe "::indirect?" do
47
+ context "INDIRECT_NODE defined and true" do
48
+ it "returns true" do
49
+ new_node = Class.new(Node)
50
+ new_node.const_set(:INDIRECT_NODE, true)
51
+ new_node.should be_indirect
52
+ end
53
+ end
54
+
55
+ context "INDIRECT_NODE not defined" do
56
+ it "returns false" do
57
+ Node.should_not be_indirect
58
+ end
59
+ end
60
+
61
+ context "INDIRECT_NODE false" do
62
+ it "returns false" do
63
+ new_node = Class.new(Node)
64
+ new_node.const_set(:INDIRECT_NODE, false)
65
+ new_node.should_not be_indirect
66
+ end
67
+ end
68
+ end
69
+
70
+ describe "#indirect?" do
71
+ context "node is indirect" do
72
+ it "returns true" do
73
+ new_node = Class.new(Node)
74
+ new_node.const_set(:INDIRECT_NODE, true)
75
+ new_node.new.should be_indirect
76
+ end
77
+ end
78
+
79
+ context "node is direct" do
80
+ it "returns false" do
81
+ Node.new.should_not be_indirect
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#node_type" do
87
+ it "returns NODE_TYPE" do
88
+ new_node = Class.new(Node)
89
+ new_node.const_set(:RJR_NODE_TYPE, 'nt')
90
+ new_node.new.node_type.should == 'nt'
91
+ end
92
+ end
93
+
94
+ describe "#initialize" do
95
+ it "sets node id" do
96
+ node = Node.new :node_id => 'node1'
97
+ node.node_id.should == 'node1'
98
+ end
99
+
100
+ it "sets dispatcher" do
101
+ dispatcher = Dispatcher.new
102
+ node = Node.new :dispatcher => dispatcher
103
+ node.dispatcher.should == dispatcher
104
+ end
105
+
106
+ context "dispatcher not specified" do
107
+ it "initializes dispatcher" do
108
+ Node.new.dispatcher.should be_an_instance_of(Dispatcher)
109
+ end
110
+ end
111
+
112
+ it "sets message headers" do
113
+ headers = {:header1 => :val}
114
+ node = Node.new :headers => headers
115
+ node.message_headers.should == headers
116
+ end
117
+
118
+ context "headers not specified" do
119
+ it "initializes blank headers" do
120
+ Node.new.message_headers.should == {}
121
+ end
122
+ end
123
+
124
+ it "initializes shared thread pool" do
125
+ Node.new
126
+ Node.tp.should_not be_nil
127
+ orig = Node.tp
128
+
129
+ Node.new.tp.should == orig
130
+ Node.tp.should == orig
131
+ end
132
+
133
+ it "initializes shared em adapter" do
134
+ Node.new
135
+ Node.em.should_not be_nil
136
+ orig = Node.em
137
+
138
+ Node.new.em.should == orig
139
+ Node.em.should == orig
140
+ end
141
+
142
+ it "starts thread pool" do
143
+ tp = ThreadPool.new
144
+ Node.should_receive(:tp).and_return(tp)
145
+ tp.should_receive(:start)
146
+ Node.new
147
+ end
148
+
149
+ it "starts em adapter" do
150
+ em = EMAdapter.new
151
+ Node.should_receive(:em).and_return(em)
152
+ em.should_receive(:start)
153
+ Node.new
154
+ end
53
155
  end
54
156
 
55
- it "should create a new dispatcher" do
56
- node = Node.new
57
- node.dispatcher.should_not be_nil
58
- node.dispatcher.class.should == Dispatcher
157
+ describe "#join" do
158
+ it "joins thread pool & em adapter and returns self" do
159
+ Node.tp.should_receive(:join)
160
+ Node.em.should_receive(:join)
161
+ n = Node.new
162
+ n.join.should == n
163
+ end
59
164
  end
60
165
 
61
- it "should start the thread pool" do
62
- node = Node.new
63
- node.class.class_variable_get(:@@tp).should be_running
166
+ describe "#halt" do
167
+ it "stops thread pool & em adatper and returns self" do
168
+ Node.tp.should_receive(:stop)
169
+ Node.em.should_receive(:stop_event_loop)
170
+ n = Node.new
171
+ n.halt.should == n
172
+ end
64
173
  end
65
174
 
66
- it "should start event machine" do
67
- EventMachine.stop_event_loop
68
- node = Node.new
69
- EventMachine.reactor_running?.should be_true
175
+ describe "#clear_event_handlers" do
176
+ it "resets connection event handlers" do
177
+ n = Node.new
178
+ n.on(:error) {}
179
+ n.clear_event_handlers
180
+ n.connection_event_handlers.should == {:closed => [], :error => []}
181
+ end
70
182
  end
71
183
 
72
- it "should halt the thread pool" do
73
- node = Node.new
74
- node.halt.join
75
- node.class.class_variable_get(:@@tp).should_not be_running
184
+ describe "#on" do
185
+ it "registers new connection event handler" do
186
+ bl = proc{}
187
+ n = Node.new
188
+ n.on(:error, &bl)
189
+ n.connection_event_handlers.should == {:closed => [], :error => [bl]}
190
+ end
76
191
  end
77
192
 
78
- it "should halt event machine" do
79
- node = Node.new
80
- node.halt.join
81
- EventMachine.reactor_running?.should be_false
193
+ describe "#connection_event" do
194
+ it "invokes event handlers" do
195
+ bl = proc{}
196
+ n = Node.new
197
+ n.on(:error, &bl)
198
+ bl.should_receive(:call).with(n)
199
+ n.send :connection_event, :error
200
+ end
82
201
  end
83
202
 
84
- it "should handle connection events" do
85
- node = Node.new
86
- closed = false
87
- error = false
88
- node.on :closed do
89
- closed = true
203
+ describe "#client_for" do
204
+ context "node is indirect" do
205
+ it "returns nil" do
206
+ n = Node.new
207
+ n.should_receive(:indirect?).and_return(true)
208
+ n.send(:client_for, 'whatever').should == [nil, nil]
209
+ end
90
210
  end
91
- node.on :error do
92
- error = true
211
+
212
+ context "node is local" do
213
+ it "returns nil" do
214
+ n = Node.new
215
+ n.should_receive(:node_type).and_return(:local)
216
+ n.send(:client_for, 'whatever').should == [nil, nil]
217
+ end
93
218
  end
94
- node.send(:connection_event, :error)
95
- error.should be_true
96
219
 
97
- node.send(:connection_event, :closed)
98
- closed.should be_true
220
+ it "extracts & returns client ip and port from socket connection" do
221
+ n = Node.new
222
+ n.should_receive(:node_type).and_return(:tcp)
223
+
224
+ c = Object.new
225
+ c.should_receive(:get_peername).and_return('peername')
226
+ expected = ['127.0.0.1', 5000]
227
+ Socket.should_receive(:unpack_sockaddr_in).
228
+ with('peername').and_return(expected)
229
+ n.send(:client_for, c).should == expected
230
+ end
231
+
232
+ context "error during ip/port extraction" do
233
+ it "returns nil" do
234
+ n = Node.new
235
+ n.should_receive(:node_type).and_return(:tcp)
236
+
237
+ c = Object.new
238
+ c.should_receive(:get_peername).and_raise(Exception)
239
+ n.send(:client_for, c).should == [nil, nil]
240
+ end
241
+ end
99
242
  end
100
243
 
101
- it "should handle request messages"
102
- it "should handle notification messages"
103
- it "should handle response messages"
104
- it "should block until reponse is received"
105
- end
244
+ describe "#handle_message" do
245
+ context "request message" do
246
+ it "handles request via thread pool" do
247
+ connection = Object.new
248
+ request = Messages::Request.new :id => 1, :method => 'foo'
249
+ request_str = request.to_s
250
+
251
+ node = Node.new
252
+ node.tp.should_receive(:<<) { |job|
253
+ job.should be_an_instance_of(ThreadPoolJob)
254
+ job.exec Mutex.new
255
+ }
256
+
257
+ node.should_receive(:handle_request).with(request_str, false, connection)
258
+ node.send :handle_message, request_str, connection
259
+ end
260
+ end
261
+
262
+ context "notification message" do
263
+ it "handles notification via thread pool" do
264
+ connection = Object.new
265
+ notification = Messages::Notification.new :method => 'foo'
266
+ notification_str = notification.to_s
106
267
 
107
- describe NodeCallback do
108
- it "should send notifications"
109
- end
110
- end
268
+ node = Node.new
269
+ node.tp.should_receive(:<<) { |job|
270
+ job.should be_an_instance_of(ThreadPoolJob)
271
+ job.exec Mutex.new
272
+ }
273
+
274
+ node.should_receive(:handle_request).
275
+ with(notification_str, true, connection)
276
+ node.send :handle_message, notification_str, connection
277
+ end
278
+ end
279
+
280
+ context "response message" do
281
+ it "handles response" do
282
+ response = Messages::Response.new :result => Result.new
283
+ response_str = response.to_s
284
+
285
+ node = Node.new
286
+ node.should_receive(:handle_response).with(response_str)
287
+ node.send :handle_message, response_str
288
+ end
289
+ end
290
+
291
+ context "anything else" do
292
+ it "does nothing" do
293
+ node = Node.new
294
+ node.tp.should_not_receive(:<<)
295
+ node.should_not_receive(:handle_request)
296
+ node.should_not_receive(:handle_response)
297
+ node.send :handle_message, "{}"
298
+ end
299
+ end
300
+ end
301
+
302
+ describe "#handle_request" do
303
+ before(:each) do
304
+ notification =
305
+ Messages::Notification.new :method => 'rjr_method1',
306
+ :args => ['method', 'args'],
307
+ :headers => {'msg' => 'headers'}
308
+ @notification = notification.to_s
309
+ @node = Node.new
310
+ end
311
+
312
+ it "invokes dispatcher.dispatch" do
313
+ @node.dispatcher.should_receive(:dispatch)
314
+ @node.send :handle_request, @notification, true
315
+ end
316
+
317
+ describe "dispatcher.dispatch args" do
318
+ it "includes :rjr_method => msg.jr_method" do
319
+ @node.dispatcher.should_receive(:dispatch) { |args|
320
+ args[:rjr_method].should == 'rjr_method1'
321
+ }
322
+ @node.send :handle_request, @notification, true
323
+ end
324
+
325
+ it "includes :rjr_method_args => msg.jr_args" do
326
+ @node.dispatcher.should_receive(:dispatch) { |args|
327
+ args[:rjr_method_args].should == ['method', 'args']
328
+ }
329
+ @node.send :handle_request, @notification, true
330
+ end
331
+
332
+ it "includes :rjr_headers => msg.headers" do
333
+ @node.dispatcher.should_receive(:dispatch) { |args|
334
+ args[:rjr_headers].should == {'msg' => 'headers'}
335
+ }
336
+ @node.send :handle_request, @notification, true
337
+ end
338
+
339
+ it "includes :rjr_client_ip => extracted client ip" do
340
+ connection = Object.new
341
+ @node.should_receive(:client_for).with(connection).
342
+ and_return([9999, '127.0.0.1'])
343
+ @node.dispatcher.should_receive(:dispatch) { |args|
344
+ args[:rjr_client_ip].should == '127.0.0.1'
345
+ }
346
+ @node.send :handle_request, @notification, true, connection
347
+ end
348
+
349
+ it "includes :rjr_client_port => extracted client port" do
350
+ connection = Object.new
351
+ @node.should_receive(:client_for).with(connection).
352
+ and_return([9999, '127.0.0.1'])
353
+ @node.dispatcher.should_receive(:dispatch) { |args|
354
+ args[:rjr_client_port].should == 9999
355
+ }
356
+ @node.send :handle_request, @notification, true, connection
357
+ end
358
+
359
+ it "includes :rjr_node => self" do
360
+ @node.dispatcher.should_receive(:dispatch) { |args|
361
+ args[:rjr_node].should == @node
362
+ }
363
+ @node.send :handle_request, @notification, true
364
+ end
365
+
366
+ it "includes :rjr_node_id => self.node_id" do
367
+ @node.should_receive(:node_id).and_return('node_id')
368
+ @node.dispatcher.should_receive(:dispatch) { |args|
369
+ args[:rjr_node_id].should == 'node_id'
370
+ }
371
+ @node.send :handle_request, @notification, true
372
+ end
373
+
374
+ it "includes :rjr_node_type => self.node_type" do
375
+ @node.should_receive(:node_type).at_least(:once).and_return(:nt)
376
+ @node.dispatcher.should_receive(:dispatch) { |args|
377
+ args[:rjr_node_type].should == :nt
378
+ }
379
+ @node.send :handle_request, @notification, true
380
+ end
381
+
382
+ it "includes :rjr_callback => constructed node callback" do
383
+ @node.dispatcher.should_receive(:dispatch) { |args|
384
+ args[:rjr_callback].should be_an_instance_of(NodeCallback)
385
+ }
386
+ @node.send :handle_request, @notification, true
387
+ end
388
+
389
+ describe "specified node callback" do
390
+ it "has a handle to the node" do
391
+ @node.dispatcher.should_receive(:dispatch) { |args|
392
+ args[:rjr_callback].node.should == @node
393
+ }
394
+ @node.send :handle_request, @notification, true
395
+ end
396
+
397
+ it "has a handle to the connection" do
398
+ connection = Object.new
399
+ @node.dispatcher.should_receive(:dispatch) { |args|
400
+ args[:rjr_callback].connection.should == connection
401
+ }
402
+ @node.send :handle_request, @notification, true, connection
403
+ end
404
+ end
405
+ end
406
+
407
+ context "handling request / not a notification" do
408
+ before(:each) do
409
+ request = Messages::Request.new :method => 'method1',
410
+ :args => ['method', 'args'],
411
+ :headers => {'msg' => 'headers'}
412
+ @request = request.to_s
413
+
414
+ result = Result.new
415
+ @expected = Messages::Response.new :result => result,
416
+ :headers => {'msg' => 'headers'},
417
+ :id => request.msg_id
418
+
419
+ # stub out dispatch
420
+ @node.dispatcher.should_receive(:dispatch).and_return(result)
421
+ end
422
+
423
+ it "sends response msg string via connection" do
424
+ connection = Object.new
425
+ @node.should_receive(:send_msg) { |response,econnection|
426
+ response.should == @expected.to_s
427
+ econnection.should == connection
428
+ }
429
+ @node.send :handle_request, @request, false, connection
430
+ end
431
+
432
+ it "returns response" do
433
+ @node.should_receive(:send_msg) # stub out
434
+ response = @node.send(:handle_request, @request, false)
435
+ response.should be_an_instance_of(Messages::Response)
436
+ response.to_s.should == @expected.to_s
437
+ end
438
+ end
439
+
440
+ it "returns nil" do
441
+ @node.send(:handle_request, @notification, true).should be_nil
442
+ end
443
+ end
444
+
445
+ describe "#handle_response" do
446
+ before(:each) do
447
+ @node = Node.new
448
+
449
+ @result = Result.new :result => 42
450
+ response = Messages::Response.new :id => 'msg1', :result => @result
451
+ @response = response.to_s
452
+ end
453
+
454
+ it "invokes dispatcher.handle_response with response result" do
455
+ @node.dispatcher.should_receive(:handle_response) { |r|
456
+ r.should be_an_instance_of(Result)
457
+ r.result.should == 42
458
+ }
459
+ @node.send :handle_response, @response
460
+ end
461
+
462
+ it "adds response msg_id and result to response queue" do
463
+ @node.send :handle_response, @response
464
+ responses = @node.instance_variable_get(:@responses)
465
+ responses.size.should == 1
466
+ responses.first[0].should == 'msg1'
467
+ responses.first[1].should == 42
468
+ end
469
+
470
+ context "response contains error" do
471
+ it "adds response error to response queue" do
472
+ @node.dispatcher.should_receive(:handle_response).and_raise(Exception)
473
+ @node.send :handle_response, @response
474
+ responses = @node.instance_variable_get(:@responses)
475
+ responses.first[2].should be_an_instance_of(Exception)
476
+ end
477
+ end
478
+
479
+ it "signals response cv" do
480
+ @node.instance_variable_get(:@response_cv).should_receive(:broadcast)
481
+ @node.send :handle_response, @response
482
+ end
483
+ end
484
+
485
+ describe "#wait_for_result" do
486
+ before(:each) do
487
+ @node = Node.new
488
+ @msg = Messages::Request.new :method => 'method1', :args => []
489
+ @response = [@msg.msg_id, 42]
490
+ end
491
+
492
+ context "response in response queue" do
493
+ before(:each) do
494
+ @node.instance_variable_set(:@responses, [@response])
495
+ end
496
+
497
+ it "deletes response from queue" do
498
+ @node.send :wait_for_result, @msg
499
+ @node.instance_variable_get(:@responses).should be_empty
500
+ end
501
+
502
+ it "returns response" do
503
+ @node.send(:wait_for_result, @msg).should == @response
504
+ end
505
+ end
506
+
507
+ context "response not in response queue" do
508
+ it "waits on response cv" do
509
+ node = Node.new
510
+ node.instance_variable_get(:@response_cv).stub(:wait) {
511
+ node.instance_variable_set(:@responses, [@response])
512
+ }
513
+ node.instance_variable_get(:@response_cv).should_receive(:wait).once
514
+ node.send(:wait_for_result, @msg)
515
+ end
516
+ end
517
+ end
518
+ end # describe Node
519
+ end # module RJR