rubarb 0.2.0

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.
@@ -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