rjr 0.18.2 → 0.19.1

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 (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