toq 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ require 'ap'
2
+ require_relative '../../lib/toq'
3
+
4
+ def pems_path
5
+ File.expand_path( File.dirname( __FILE__ ) + '/../' )
6
+ end
7
+
8
+ def rpc_opts
9
+ {
10
+ host: '127.0.0.1',
11
+ port: 7331,
12
+ token: 'superdupersecret',
13
+ serializer: Marshal,
14
+ }
15
+ end
16
+
17
+ def rpc_opts_with_socket
18
+ opts = rpc_opts
19
+ opts.delete( :host )
20
+ opts.delete( :port )
21
+
22
+ opts.merge( socket: '/tmp/arachni-rpc-test' )
23
+ end
24
+
25
+ def rpc_opts_with_ssl_primitives
26
+ rpc_opts.merge(
27
+ port: 7332,
28
+ ssl_ca: pems_path + '/pems/cacert.pem',
29
+ ssl_pkey: pems_path + '/pems/client/key.pem',
30
+ ssl_cert: pems_path + '/pems/client/cert.pem'
31
+ )
32
+ end
33
+
34
+ def rpc_opts_with_invalid_ssl_primitives
35
+ rpc_opts_with_ssl_primitives.merge(
36
+ ssl_pkey: pems_path + '/pems/client/foo-key.pem',
37
+ ssl_cert: pems_path + '/pems/client/foo-cert.pem'
38
+ )
39
+ end
40
+
41
+ def rpc_opts_with_mixed_ssl_primitives
42
+ rpc_opts_with_ssl_primitives.merge(
43
+ ssl_pkey: pems_path + '/pems/client/key.pem',
44
+ ssl_cert: pems_path + '/pems/client/foo-cert.pem'
45
+ )
46
+ end
47
+
48
+ class Parent
49
+ def foo( arg )
50
+ arg
51
+ end
52
+ end
53
+
54
+ class Test < Parent
55
+
56
+ # In order to make inherited methods accessible you've got to explicitly
57
+ # make them public.
58
+ private :foo
59
+ public :foo
60
+
61
+ def delay( arg, &block )
62
+ Raktr.global.delay( 1 ) { block.call( arg ) }
63
+ end
64
+
65
+ def exception
66
+ fail
67
+ end
68
+
69
+ def defer( arg, &block )
70
+ Thread.new do
71
+ block.call( arg )
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ def start_server( opts, do_not_start = false )
78
+ server = Toq::Server.new( opts )
79
+ server.add_async_check { |method| method.parameters.flatten.include? :block }
80
+ server.add_handler( 'test', Test.new )
81
+ server.run if !do_not_start
82
+ server
83
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'server'
2
+
3
+ opts = rpc_opts.merge(
4
+ socket: '/tmp/arachni-rpc-test',
5
+ serializer: Marshal
6
+ )
7
+
8
+ start_server( opts )
@@ -0,0 +1,11 @@
1
+ require_relative 'server'
2
+
3
+ cwd = File.expand_path( File.dirname( __FILE__ ) )
4
+ opts = rpc_opts.merge(
5
+ port: 7332,
6
+ ssl_ca: cwd + '/../pems/cacert.pem',
7
+ ssl_pkey: cwd + '/../pems/server/key.pem',
8
+ ssl_cert: cwd + '/../pems/server/cert.pem'
9
+ )
10
+
11
+ start_server( opts )
@@ -0,0 +1,39 @@
1
+ require 'ap'
2
+ require 'timeout'
3
+ require_relative '../lib/toq'
4
+ require_relative 'servers/server'
5
+
6
+ def cwd
7
+ File.expand_path( File.dirname( __FILE__ ) )
8
+ end
9
+
10
+ def start_client( opts )
11
+ Toq::Client.new( opts )
12
+ end
13
+
14
+ def quiet_spawn( file )
15
+ path = File.join( File.expand_path( File.dirname( __FILE__ ) ), 'servers', "#{file}.rb" )
16
+ Process.spawn RbConfig.ruby, path#, out: '/dev/null'
17
+ end
18
+
19
+ server_pids = []
20
+ RSpec.configure do |config|
21
+ config.color = true
22
+ config.add_formatter :documentation
23
+
24
+ config.before( :suite ) do
25
+ File.delete( '/tmp/arachni-rpc-test' ) rescue nil
26
+
27
+ files = %w(basic with_ssl_primitives)
28
+ files << 'unix_socket' if Raktr.supports_unix_sockets?
29
+
30
+ files.each do |name|
31
+ server_pids << quiet_spawn( name ).tap { |pid| Process.detach( pid ) }
32
+ end
33
+ sleep 5
34
+ end
35
+
36
+ config.after( :suite ) do
37
+ server_pids.each { |pid| Process.kill( 'KILL', pid ) }
38
+ end
39
+ end
@@ -0,0 +1,397 @@
1
+ require 'spec_helper'
2
+
3
+ describe Toq::Client do
4
+
5
+ def wait
6
+ Raktr.global.wait rescue Raktr::Error::NotRunning
7
+ end
8
+
9
+ before(:each) do
10
+ if Raktr.global.running?
11
+ Raktr.stop
12
+ end
13
+ end
14
+
15
+ let(:arguments) do
16
+ [
17
+ 'one',
18
+ 2,
19
+ { three: 3 },
20
+ [ 4 ]
21
+ ]
22
+ end
23
+ let(:reactor) { Raktr.global }
24
+ let(:handler) { 'test' }
25
+ let(:remote_method) { 'foo' }
26
+ let(:options) { rpc_opts }
27
+ subject do
28
+ start_client( options )
29
+ end
30
+
31
+ def call( &block )
32
+ subject.call( "#{handler}.#{remote_method}", arguments, &block )
33
+ end
34
+
35
+ describe '#initialize' do
36
+ let(:options) { rpc_opts.merge( role: :client ) }
37
+
38
+ it 'assigns instance options (including :role)' do
39
+ subject.opts.should == options
40
+ end
41
+
42
+ context 'when passed no connection information' do
43
+ it 'raises ArgumentError' do
44
+ begin
45
+ described_class.new({})
46
+ rescue => e
47
+ e.should be_kind_of ArgumentError
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'option' do
53
+ describe :socket, if: Raktr.supports_unix_sockets? do
54
+ let(:options) { rpc_opts_with_socket }
55
+
56
+ it 'connects to it' do
57
+ call.should == arguments
58
+ end
59
+
60
+ context 'when under heavy load' do
61
+ it 'retains stability and consistency' do
62
+ n = 10_000
63
+ cnt = 0
64
+
65
+ mismatches = []
66
+
67
+ n.times do |i|
68
+ arg = 'a' * i
69
+ subject.call( "#{handler}.#{remote_method}", arg ) do |res|
70
+ cnt += 1
71
+ mismatches << [i, arg, res] if arg != res
72
+ Raktr.stop if cnt == n || mismatches.any?
73
+ end
74
+ end
75
+ wait
76
+
77
+ cnt.should > 0
78
+ mismatches.should be_empty
79
+ end
80
+ end
81
+
82
+ context 'and connecting to a non-existent server' do
83
+ let(:options) { rpc_opts_with_socket.merge( socket: '/' ) }
84
+
85
+ it "returns #{Toq::Exceptions::ConnectionError}" do
86
+ response = nil
87
+ call do |res|
88
+ response = res
89
+ Raktr.stop
90
+ end
91
+ wait
92
+
93
+ response.should be_rpc_connection_error
94
+ response.should be_kind_of Toq::Exceptions::ConnectionError
95
+ end
96
+ end
97
+
98
+ context 'when passed an invalid socket path' do
99
+ it 'raises ArgumentError' do
100
+ begin
101
+ described_class.new( socket: 'blah' )
102
+ rescue => e
103
+ e.should be_kind_of ArgumentError
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'when passed a host but not a port' do
111
+ it 'raises ArgumentError' do
112
+ begin
113
+ described_class.new( host: 'test' )
114
+ rescue => e
115
+ e.should be_kind_of ArgumentError
116
+ end
117
+ end
118
+ end
119
+
120
+ context 'when passed a port but not a host' do
121
+ it 'raises ArgumentError' do
122
+ begin
123
+ described_class.new( port: 9999 )
124
+ rescue => e
125
+ e.should be_kind_of ArgumentError
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'when passed an invalid port' do
131
+ it 'raises ArgumentError' do
132
+ begin
133
+ described_class.new( host: 'tt', port: 'blah' )
134
+ rescue => e
135
+ e.should be_kind_of ArgumentError
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ describe '#call' do
142
+ context 'when calling a remote method that delays its results' do
143
+ let(:remote_method) { 'delay' }
144
+
145
+ it 'it supports it' do
146
+ call.should == arguments
147
+ end
148
+ end
149
+
150
+ context 'when calling a remote method that defers its results' do
151
+ let(:remote_method) { 'defer' }
152
+
153
+ it 'it supports it' do
154
+ call.should == arguments
155
+ end
156
+ end
157
+
158
+ context 'when under heavy load' do
159
+ it 'retains stability and consistency' do
160
+ n = 10_000
161
+ cnt = 0
162
+
163
+ mismatches = []
164
+
165
+ n.times do |i|
166
+ arg = 'a' * i
167
+ subject.call( "#{handler}.#{remote_method}", arg ) do |res|
168
+ cnt += 1
169
+ mismatches << [i, arg, res] if arg != res
170
+ Raktr.stop if cnt == n || mismatches.any?
171
+ end
172
+ end
173
+
174
+ wait
175
+
176
+ cnt.should > 0
177
+ mismatches.should be_empty
178
+ end
179
+ end
180
+
181
+ context 'when using Threads' do
182
+ it 'should be able to perform synchronous calls' do
183
+ arguments.should == call
184
+ end
185
+
186
+ it 'should be able to perform asynchronous calls' do
187
+ response = nil
188
+ call do |res|
189
+ response = res
190
+ Raktr.stop
191
+ end
192
+ wait
193
+
194
+ response.should == arguments
195
+ end
196
+ end
197
+
198
+ context 'when run inside the Reactor loop' do
199
+ it 'should be able to perform asynchronous calls' do
200
+ response = nil
201
+
202
+ Raktr.stop
203
+ reactor.run do
204
+ call do |res|
205
+ response = res
206
+ Raktr.stop
207
+ end
208
+ end
209
+
210
+ response.should == arguments
211
+ end
212
+
213
+ it 'should not be able to perform synchronous calls' do
214
+ exception = nil
215
+
216
+ Raktr.stop
217
+ reactor.run do
218
+ begin
219
+ call
220
+ rescue => e
221
+ exception = e
222
+ Raktr.stop
223
+ end
224
+ end
225
+
226
+ exception.should be_kind_of RuntimeError
227
+ end
228
+ end
229
+
230
+ context 'when performing an asynchronous call' do
231
+ context 'and connecting to a non-existent server' do
232
+ let(:options) { rpc_opts.merge( host: 'dddd', port: 999339 ) }
233
+
234
+ it "returns #{Toq::Exceptions::ConnectionError}" do
235
+ response = nil
236
+ call do |res|
237
+ response = res
238
+ Raktr.stop
239
+ end
240
+ wait
241
+
242
+ response.should be_rpc_connection_error
243
+ response.should be_kind_of Toq::Exceptions::ConnectionError
244
+ end
245
+ end
246
+
247
+ context 'and requesting a non-existent object' do
248
+ let(:handler) { 'bar' }
249
+
250
+ it "returns #{Toq::Exceptions::InvalidObject}" do
251
+ response = nil
252
+
253
+ call do |res|
254
+ response = res
255
+ Raktr.stop
256
+ end
257
+ wait
258
+
259
+ response.should be_rpc_invalid_object_error
260
+ response.should be_kind_of Toq::Exceptions::InvalidObject
261
+ end
262
+ end
263
+
264
+ context 'and requesting a non-public method' do
265
+ let(:remote_method) { 'bar' }
266
+
267
+ it "returns #{Toq::Exceptions::InvalidMethod}" do
268
+ response = nil
269
+
270
+ call do |res|
271
+ response = res
272
+ Raktr.stop
273
+ end
274
+ wait
275
+
276
+ response.should be_rpc_invalid_method_error
277
+ response.should be_kind_of Toq::Exceptions::InvalidMethod
278
+ end
279
+ end
280
+
281
+ context 'and there is a remote exception' do
282
+ let(:remote_method) { :exception }
283
+
284
+ it "returns #{Toq::Exceptions::RemoteException}" do
285
+ response = nil
286
+ call do |res|
287
+ response = res
288
+ Raktr.stop
289
+ end
290
+ wait
291
+
292
+ response.should be_rpc_remote_exception
293
+ response.should be_kind_of Toq::Exceptions::RemoteException
294
+ end
295
+ end
296
+ end
297
+
298
+ context 'when performing a synchronous call' do
299
+ context 'and connecting to a non-existent server' do
300
+ let(:options) { rpc_opts.merge( host: 'dddd', port: 999339 ) }
301
+
302
+ it "raises #{Toq::Exceptions::ConnectionError}" do
303
+ begin
304
+ call
305
+ rescue => e
306
+ e.rpc_connection_error?.should be_true
307
+ e.should be_kind_of Toq::Exceptions::ConnectionError
308
+ end
309
+ end
310
+ end
311
+
312
+ context 'and requesting a non-existent object' do
313
+ let(:handler) { 'bar' }
314
+
315
+ it "raises #{Toq::Exceptions::InvalidObject}" do
316
+ begin
317
+ call
318
+ rescue => e
319
+ e.rpc_invalid_object_error?.should be_true
320
+ e.should be_kind_of Toq::Exceptions::InvalidObject
321
+ end
322
+ end
323
+ end
324
+
325
+ context 'and requesting a non-public method' do
326
+ let(:remote_method) { 'bar' }
327
+
328
+ it "raises #{Toq::Exceptions::InvalidMethod}" do
329
+ begin
330
+ call
331
+ rescue => e
332
+ e.rpc_invalid_method_error?.should be_true
333
+ e.should be_kind_of Toq::Exceptions::InvalidMethod
334
+ end
335
+ end
336
+ end
337
+
338
+ context 'and there is a remote exception' do
339
+ let(:remote_method) { :exception }
340
+
341
+ it "raises #{Toq::Exceptions::RemoteException}" do
342
+ begin
343
+ call
344
+ rescue => e
345
+ e.rpc_remote_exception?.should be_true
346
+ e.should be_kind_of Toq::Exceptions::RemoteException
347
+ end
348
+ end
349
+ end
350
+ end
351
+
352
+ context 'when using valid SSL configuration' do
353
+ let(:options) { rpc_opts_with_ssl_primitives }
354
+
355
+ it 'should be able to establish a connection' do
356
+ call.should == arguments
357
+ end
358
+ end
359
+
360
+ context 'when using invalid SSL configuration' do
361
+ let(:options) { rpc_opts_with_invalid_ssl_primitives }
362
+
363
+ it 'should not be able to establish a connection' do
364
+ response = nil
365
+
366
+ Raktr.stop
367
+ reactor.run do
368
+ call do |res|
369
+ response = res
370
+ Raktr.stop
371
+ end
372
+ end
373
+
374
+ response.should be_rpc_connection_error
375
+ end
376
+ end
377
+
378
+ context 'when using mixed SSL configuration' do
379
+ let(:options) { rpc_opts_with_mixed_ssl_primitives }
380
+
381
+ it 'should not be able to establish a connection' do
382
+ response = nil
383
+
384
+ Raktr.stop
385
+ reactor.run do
386
+ call do |res|
387
+ response = res
388
+ Raktr.stop
389
+ end
390
+ end
391
+
392
+ response.should be_rpc_connection_error
393
+ end
394
+ end
395
+ end
396
+
397
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ describe Toq::Exceptions do
4
+
5
+ describe '#rpc_exception?' do
6
+ context 'for RPC exceptions' do
7
+ subject { described_class::InvalidMethod.new.rpc_exception? }
8
+ it { should be_true }
9
+ end
10
+
11
+ context 'for other exceptions' do
12
+ subject { ::Exception.new.rpc_connection_error? }
13
+ it { should be_false }
14
+ end
15
+ end
16
+
17
+ describe '#rpc_connection_error?' do
18
+ context 'for ConnectionError' do
19
+ subject { described_class::ConnectionError.new.rpc_connection_error? }
20
+ it { should be_true }
21
+ end
22
+
23
+ context 'for other exceptions' do
24
+ subject { described_class::InvalidMethod.new.rpc_connection_error? }
25
+ it { should be_false }
26
+ end
27
+ end
28
+
29
+ describe '#rpc_remote_exception?' do
30
+ context 'for RemoteException' do
31
+ subject { described_class::RemoteException.new.rpc_remote_exception? }
32
+ it { should be_true }
33
+ end
34
+
35
+ context 'for other exceptions' do
36
+ subject { described_class::InvalidMethod.new.rpc_remote_exception? }
37
+ it { should be_false }
38
+ end
39
+ end
40
+
41
+ describe '#rpc_invalid_object_error?' do
42
+ context 'for invalid object RPC exceptions' do
43
+ subject { described_class::InvalidObject.new.rpc_invalid_object_error? }
44
+ it { should be_true }
45
+ end
46
+
47
+ context 'for other exceptions' do
48
+ subject { ::Exception.new.rpc_invalid_object_error? }
49
+ it { should be_false }
50
+ end
51
+ end
52
+
53
+ describe '#rpc_invalid_method_error?' do
54
+ context 'for invalid method RPC exceptions' do
55
+ subject { described_class::InvalidMethod.new.rpc_invalid_method_error? }
56
+ it { should be_true }
57
+ end
58
+
59
+ context 'for other exceptions' do
60
+ subject { ::Exception.new.rpc_invalid_method_error? }
61
+ it { should be_false }
62
+ end
63
+ end
64
+
65
+ describe '#rpc_invalid_token_error?' do
66
+ context 'for RPC exceptions' do
67
+ subject { described_class::InvalidToken.new.rpc_invalid_token_error? }
68
+ it { should be_true }
69
+ end
70
+
71
+ context 'for other exceptions' do
72
+ subject { ::Exception.new.rpc_invalid_token_error? }
73
+ it { should be_false }
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ class MyMessage < Toq::Message
4
+ attr_accessor :foo
5
+ attr_accessor :boo
6
+
7
+ def transmit?( attr )
8
+ attr == :@boo
9
+ end
10
+ end
11
+
12
+ describe Toq::Message do
13
+ let(:options) { { foo: 'foo val', boo: 'boo val' }}
14
+ subject { MyMessage.new( options ) }
15
+
16
+ describe '#initialize' do
17
+ it 'sets attributes' do
18
+ subject.foo == options[:foo]
19
+ subject.boo == options[:boo]
20
+ end
21
+ end
22
+
23
+ describe '#merge!' do
24
+ it 'assigns the attribute values of the provided object to self' do
25
+ opts = { foo: 'my foo' }
26
+ my_msg = MyMessage.new( opts )
27
+
28
+ subject.merge!( my_msg )
29
+
30
+ subject.foo == opts[:foo]
31
+ subject.boo == options[:boo]
32
+ end
33
+ end
34
+
35
+ describe '#prepare_for_tx' do
36
+ it 'converts self into a hash' do
37
+ subject.prepare_for_tx.class.should == Hash
38
+ end
39
+
40
+ it 'skips attributes based on #transmit?' do
41
+ subject.prepare_for_tx.should include 'boo'
42
+ subject.prepare_for_tx.should_not include 'callback_id'
43
+ subject.prepare_for_tx.should_not include 'foo'
44
+ end
45
+ end
46
+
47
+ end