rubarb 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,261 @@
1
+ require 'rubarb/server'
2
+ require 'rubarb/connection'
3
+ require 'rubarb/insecure_method_call_error'
4
+ require 'socket'
5
+
6
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
7
+
8
+ describe "Server Failures" do
9
+
10
+ CONNECTION_ERROR = "Connection Failure"
11
+ NO_METHOD_ERROR = "received unexpected message :not_a_method"
12
+ INSECURE_METHOD_ERROR = "Remote client attempts to call method class, but was denied."
13
+
14
+ before(:all) do
15
+ @port = 9441
16
+ end
17
+
18
+ before(:each) do
19
+ @server = Rubarb::Server.new("127.0.0.1", @port, mock("server"))
20
+ @connection1 = Rubarb::Connection.new("127.0.0.1", @port, mock("client"))
21
+ @connection2 = Rubarb::Connection.new("127.0.0.1", @port, mock("client"))
22
+ end
23
+
24
+ def wait_for_connections(n, ttl, &block)
25
+ if ttl <= 0
26
+ fail("TTL expired")
27
+ end
28
+
29
+ if @cons != n
30
+ EventMachine.add_periodic_timer(0.1) do
31
+ wait_for_connections(n, ttl-1, &block)
32
+ end
33
+ else
34
+ yield
35
+ end
36
+ end
37
+
38
+ def check_messages(size, actual, expected)
39
+ actual.should have(size).items
40
+ (0...size).each { |i| actual[i].include?(expected[i]).should be_true }
41
+ end
42
+
43
+ def run_server_failure(blocks)
44
+ server_block = blocks[:server]
45
+ client_block = blocks[:client]
46
+ server_errback = blocks[:server_errback]
47
+ client_errback = blocks[:client_errback]
48
+ connected = false
49
+ errbacked = false
50
+ thread = start_reactor
51
+ EM.schedule do
52
+ @server.errback { |e| server_errback.call(e) if server_errback; errbacked = true }
53
+ @server.start { |connection| server_block.call(connection) if server_block }
54
+ @connection1.errback { |e| client_errback.call(e) if client_errback; errbacked = true }
55
+ @connection1.start { client_block.call(@connection1) if client_block; connected = true }
56
+ end
57
+ wait_for{connected}
58
+ wait_for{errbacked}
59
+ stop_reactor(thread)
60
+ end
61
+
62
+ it "should handle the loss of a client" do
63
+ EM.run do
64
+ @cons = 0
65
+ @server.start do |client|
66
+ @cons += 1
67
+ if (@cons == 2)
68
+ @connection1.stop
69
+ end
70
+ client.errback do
71
+ @cons -= 1
72
+ if @cons == 1
73
+ EM.stop
74
+ end
75
+ end
76
+ end
77
+
78
+ @connection1.start
79
+ @connection2.start
80
+
81
+ wait_for_connections(2, 10) do
82
+ @connection1.stop
83
+ wait_for_connections(1, 10) do
84
+ @cons.should == 1
85
+ EM.stop
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ it "should call errorback when port is already in use" do
92
+ errback_called = false
93
+ err_message = ""
94
+
95
+ thread = start_reactor
96
+ EM.run do
97
+ blocked_server = Rubarb::Server.new("127.0.0.1", @port, mock("server"))
98
+
99
+ blocked_server.errback do |e|
100
+ errback_called = true
101
+ err_message = e.message
102
+ end
103
+
104
+ @server.start
105
+ blocked_server.start
106
+ end
107
+
108
+ wait_for{errback_called}
109
+ stop_reactor(thread)
110
+
111
+ errback_called.should be_true
112
+ err_message.include?("acceptor").should be_true
113
+ end
114
+
115
+ it "handles no method call on server side" do
116
+ err_messages = []
117
+ expected_messages = [NO_METHOD_ERROR, CONNECTION_ERROR]
118
+ client_errback_block = Proc.new { |e| err_messages << e.message }
119
+ client_block = Proc.new { |connection| connection.not_a_method }
120
+ run_server_failure({:client => client_block, :client_errback => client_errback_block})
121
+ check_messages(2, err_messages, expected_messages)
122
+ end
123
+
124
+ it "handles no method call on client side" do
125
+ err_messages = []
126
+ expected_messages = [NO_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
127
+ server_errback_block = Proc.new { |e| err_messages << e.message }
128
+ server_block = Proc.new { |connection| connection.not_a_method }
129
+ run_server_failure({:server => server_block, :server_errback => server_errback_block})
130
+ check_messages(3, err_messages, expected_messages)
131
+ end
132
+
133
+ it "handles insecure method call on server side" do
134
+ err_messages = []
135
+ expected_messages = [INSECURE_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
136
+
137
+ client_block = Proc.new do |connection|
138
+ connection.instance_eval("undef class")
139
+ connection.class do |result|
140
+ result.should be_a(InsecureMethodCallError)
141
+ end
142
+ end
143
+
144
+ client_errback_block = Proc.new { |e| err_messages << e.message }
145
+ run_server_failure({:server => client_block, :server_errback => client_errback_block})
146
+ check_messages(3, err_messages, expected_messages)
147
+ end
148
+
149
+ it "handles insecure method call on client side" do
150
+ err_messages = []
151
+ expected_messages = [INSECURE_METHOD_ERROR, CONNECTION_ERROR, CONNECTION_ERROR]
152
+
153
+ server_block = Proc.new do |connection|
154
+ connection.instance_eval("undef class")
155
+ connection.class do |result|
156
+ result.should be_a(InsecureMethodCallError)
157
+ end
158
+ end
159
+
160
+ server_errback_block = Proc.new { |e| err_messages << e.message }
161
+ run_server_failure({:server => server_block, :server_errback => server_errback_block})
162
+ check_messages(3, err_messages, expected_messages)
163
+ end
164
+
165
+ it "removes unbinded connection from connections ivar" do
166
+ thread = start_reactor
167
+ connected = false
168
+ EM.schedule do
169
+ @server.start
170
+ @connection1.start {connected = true}
171
+ end
172
+ wait_for{connected}
173
+ @server.connections.size.times { @server.connections.first.unbind }
174
+ @server.connections.should have(0).items
175
+ stop_reactor(thread)
176
+ end
177
+
178
+ it "removes one unbinded connection from connections ivar of size two" do
179
+ thread = start_reactor
180
+ connected = false
181
+ @server.start
182
+ @connection1.start
183
+ @connection2.start {connected = true}
184
+ wait_for{connected}
185
+ 2.times { @server.connections.first.unbind }
186
+ @server.connections.should have(2).items
187
+ stop_reactor(thread)
188
+ end
189
+
190
+ it "does not error out after calling stop twice consecutively" do
191
+ EventMachine.should_receive(:stop_server).once
192
+ thread = start_reactor
193
+ connected = false
194
+ @server.start
195
+ @connection1.start {connected = true}
196
+ wait_for{connected}
197
+ stopped = []
198
+ @server.stop do |result|
199
+ stopped << result
200
+ end
201
+ @server.stop do |result|
202
+ stopped << result
203
+ end
204
+ wait_for{stopped.size == 2}
205
+ stopped.should == [true, false]
206
+ stop_reactor(thread)
207
+ end
208
+
209
+ it "executes all errback blocks when exception is thrown on client side" do
210
+ @errback1 = false
211
+ @errback2 = false
212
+ @errback3 = false
213
+ @errback4 = false
214
+
215
+ server_block = Proc.new do |connection|
216
+ connection.errback { |e| @errback1 = true }
217
+ connection.errback { |e| @errback2 = true }
218
+ connection.not_a_method
219
+ end
220
+
221
+ client_block = Proc.new do |connection|
222
+ connection.errback { |e| @errback3 = true }
223
+ connection.errback { |e| @errback4 = true }
224
+ end
225
+
226
+ run_server_failure({:server => server_block,
227
+ :client => client_block})
228
+
229
+ @errback1.should be_true
230
+ @errback2.should be_true
231
+ @errback3.should be_true
232
+ @errback4.should be_true
233
+ end
234
+
235
+ it "executes all errback blocks when exception is thrown on server side" do
236
+ @errback1 = false
237
+ @errback2 = false
238
+ @errback3 = false
239
+ @errback4 = false
240
+
241
+ server_block = Proc.new do |connection|
242
+ connection.errback { |e| @errback1 = true }
243
+ connection.errback { |e| @errback2 = true }
244
+ end
245
+
246
+ client_block = Proc.new do |connection|
247
+ connection.errback { |e| @errback3 = true }
248
+ connection.errback { |e| @errback4 = true }
249
+ connection.not_a_method
250
+ end
251
+
252
+ run_server_failure({:server => server_block,
253
+ :client => client_block})
254
+
255
+ @errback1.should be_true
256
+ @errback2.should be_true
257
+ @errback3.should be_true
258
+ @errback4.should be_true
259
+ end
260
+
261
+ end
@@ -0,0 +1,201 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'rubarb/server'
3
+ require "rubarb/remote_call"
4
+ require 'rubarb/connection'
5
+ require 'rubarb/default'
6
+
7
+ include Rubarb
8
+
9
+ describe Listener do
10
+ include RemoteCall
11
+
12
+ before(:each) do
13
+ extend(Listener)
14
+ @sent_data = ""
15
+ self.stub!(:send_data) do |data|
16
+ @sent_data = data
17
+ end
18
+ post_init
19
+ end
20
+
21
+ def set_id(id)
22
+ id_generator = mock("id")
23
+ id_generator.stub!(:next).and_return(id)
24
+ @conn_id_generator = id_generator
25
+ end
26
+
27
+ it "should generate and send connection ID" do
28
+ set_id("00000001")
29
+ receive_data("4")
30
+ @sent_data.should == "00000001"
31
+ end
32
+
33
+ it "should receive another connection" do
34
+ stub!(:send_message) do |message|
35
+ @sent_message = message
36
+ end
37
+ receive_data("5")
38
+ receive_data("00000005")
39
+ @sent_message.should == "00000005"
40
+ @conn_id.should == "00000005"
41
+ end
42
+
43
+ class OtherProtocol
44
+
45
+ attr_reader :buffer, :connection
46
+
47
+ def handle_connection(buffer, connection)
48
+ @buffer = buffer
49
+ @connection = connection
50
+ end
51
+
52
+ end
53
+
54
+ it "should handle other protocols" do
55
+ @external_protocol = OtherProtocol.new
56
+ receive_data("1")
57
+
58
+ @external_protocol.buffer.should == "1"
59
+ @external_protocol.connection.should_not be_nil
60
+ end
61
+ end
62
+
63
+ describe Rubarb::Server do
64
+
65
+ before(:each) do
66
+ @reactor_thread = nil
67
+ end
68
+
69
+ after(:each) do
70
+ stop_reactor(@reactor_thread) if @reactor_thread
71
+ end
72
+
73
+ it "has an instance of Rubarb::Id" do
74
+ server = Server.new("host", "port", "api")
75
+ server.conn_id_generator.class.should == Rubarb::Id
76
+ end
77
+
78
+ it "has an instance of Rubarb::Id for message ids" do
79
+ server = Server.new("host", "port", "api")
80
+ server.msg_id_generator.class.should == Rubarb::Id
81
+ end
82
+
83
+ it "has an instance of insecure_methods" do
84
+ server = Server.new("host", "port", "api")
85
+ server.insecure_methods.class.should == Array
86
+ end
87
+
88
+ it "has default insecure_methods" do
89
+ server = Server.new("host", "port", "api")
90
+ server.insecure_methods.should == Rubarb::Default::INSECURE_METHODS
91
+ end
92
+
93
+ it "accepts custom insecure methods on initilization" do
94
+ server = Server.new("host", "port", "api", [:==, :===, :=~])
95
+ server.insecure_methods.should == [:==, :===, :=~]
96
+ end
97
+
98
+ def connect
99
+ @reactor_thread = start_reactor
100
+ connected = false
101
+ @server = Rubarb::Server.new("127.0.0.1", 9441, mock("server"))
102
+ @connection = Rubarb::Connection.new("127.0.0.1", 9441, mock("client"))
103
+ EM.schedule do
104
+ @server.start { |client| @client = client }
105
+ @connection.start { connected = true }
106
+ end
107
+ wait_for { connected }
108
+ end
109
+
110
+ it "sets instance of Rubarb::Id to each connection for connection ids" do
111
+ connect
112
+ generator_class = @server.connections.first.conn_id_generator.class
113
+ generator_class.should == Rubarb::Id
114
+ end
115
+
116
+ it "sets instance of Rubarb::Id to each connection for message ids" do
117
+ connect
118
+ generator_class = @server.connections.first.msg_id_generator.class
119
+ generator_class.should == Rubarb::Id
120
+ end
121
+
122
+ it "sets instance of insecure_methods on each connection" do
123
+ connect
124
+ insecure_methods = @server.connections.first.insecure_methods
125
+ insecure_methods.should == Rubarb::Default::INSECURE_METHODS
126
+ end
127
+
128
+ it "makes sure @conn_id_generator#next is called in handle_incoming" do
129
+ extend(Listener)
130
+ id_generator = mock("id")
131
+ id_generator.stub!(:next).and_return("00000001")
132
+ @conn_id_generator = id_generator
133
+ should_receive(:send_data).with("00000001")
134
+ stub!(:switch_protocol)
135
+ handle_incoming
136
+ @conn_id.should == "00000001"
137
+ end
138
+
139
+ it "makes two overlapping calls" do
140
+ @reactor_thread = start_reactor
141
+ connected = false
142
+
143
+ @server = Rubarb::Server.new("127.0.0.1", 9441, TestApi.new)
144
+ @connection = Rubarb::Connection.new("127.0.0.1", 9441, mock("client"))
145
+ EM.run do
146
+ @server.start
147
+ @connection.start do
148
+ @connection.get_one do |counter|
149
+ counter.should == 1
150
+ end
151
+ @connection.get_two do |counter|
152
+ counter.should == 2
153
+ end
154
+ connected = true
155
+ end
156
+ end
157
+ wait_for { connected }
158
+ end
159
+
160
+ it "catches exceptions that occur during a remote call to client" do
161
+ connect
162
+ error = nil
163
+ @client.remote_connection.errbacks << proc { |e| error = e }
164
+ @client.remote_connection.should_receive(:remote_call).and_raise("Blah")
165
+
166
+ @client.foo
167
+ wait_for { error != nil }
168
+
169
+ error.to_s.should == "Blah"
170
+ EM.reactor_running?.should == true
171
+ end
172
+
173
+ it "should catch exceptions in starting server" do
174
+ EventMachine.stub!(:start_server).and_raise("EMReactor Exception")
175
+
176
+ @reactor_thread = start_reactor
177
+ done = false
178
+
179
+ @server = Rubarb::Server.new("127.0.0.1", 9441, TestApi.new)
180
+ @server.errback do |error|
181
+ error.message.should == "EMReactor Exception"
182
+ done = true
183
+ end
184
+
185
+ @server.start
186
+
187
+ wait_for { done }
188
+ done.should == true
189
+
190
+ end
191
+ end
192
+
193
+ class TestApi
194
+ def get_one(responder)
195
+ EM.add_timer(0.5) { responder.reply(1) }
196
+ end
197
+
198
+ def get_two(responder)
199
+ responder.reply(2)
200
+ end
201
+ end