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