cql-rb 1.1.0.pre0 → 1.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'spec_helper'
4
- require 'cql/client/client_shared'
5
4
 
6
5
 
7
6
  module Cql
@@ -99,6 +98,30 @@ module Cql
99
98
  client.async.should equal(async_client)
100
99
  end
101
100
  end
101
+
102
+ context 'when exceptions are raised' do
103
+ it 'replaces the backtrace of the asynchronous call to make it less confusing' do
104
+ error = CqlError.new('Bork')
105
+ error.set_backtrace(['Hello', 'World'])
106
+ future.stub(:get).and_raise(error)
107
+ async_client.stub(:execute).and_return(future)
108
+ begin
109
+ client.execute('SELECT * FROM something')
110
+ rescue CqlError => e
111
+ e.backtrace.first.should match(%r{/synchronous_client.rb:\d+:in `execute'})
112
+ end
113
+ end
114
+
115
+ it 'does not replace the backtrace of non-CqlError errors' do
116
+ future.stub(:get).and_raise('Bork')
117
+ async_client.stub(:execute).and_return(future)
118
+ begin
119
+ client.execute('SELECT * FROM something')
120
+ rescue => e
121
+ e.backtrace.first.should_not match(%r{/synchronous_client.rb:\d+:in `execute'})
122
+ end
123
+ end
124
+ end
102
125
  end
103
126
  end
104
127
  end
@@ -60,6 +60,30 @@ module Cql
60
60
  statement.async.should equal(async_statement)
61
61
  end
62
62
  end
63
+
64
+ context 'when exceptions are raised' do
65
+ it 'replaces the backtrace of the asynchronous call to make it less confusing' do
66
+ error = CqlError.new('Bork')
67
+ error.set_backtrace(['Hello', 'World'])
68
+ future.stub(:get).and_raise(error)
69
+ async_statement.stub(:execute).and_return(future)
70
+ begin
71
+ statement.execute('SELECT * FROM something')
72
+ rescue CqlError => e
73
+ e.backtrace.first.should match(%r{/synchronous_prepared_statement.rb:\d+:in `execute'})
74
+ end
75
+ end
76
+
77
+ it 'does not replace the backtrace of non-CqlError errors' do
78
+ future.stub(:get).and_raise('Bork')
79
+ async_statement.stub(:execute).and_return(future)
80
+ begin
81
+ statement.execute('SELECT * FROM something')
82
+ rescue => e
83
+ e.backtrace.first.should_not match(%r{/synchronous_prepared_statement.rb:\d+:in `execute'})
84
+ end
85
+ end
86
+ end
63
87
  end
64
88
  end
65
89
  end
@@ -223,6 +223,60 @@ module Cql
223
223
  end
224
224
  end
225
225
 
226
+ describe '.first' do
227
+ context 'it returns a new future which' do
228
+ it 'is complete when the first of the source futures is complete' do
229
+ f1 = Future.new
230
+ f2 = Future.new
231
+ f3 = Future.new
232
+ ff = Future.first(f1, f2, f3)
233
+ f2.complete!
234
+ ff.should be_complete
235
+ end
236
+
237
+ it 'completes with the value of the first source future' do
238
+ f1 = Future.new
239
+ f2 = Future.new
240
+ f3 = Future.new
241
+ ff = Future.first(f1, f2, f3)
242
+ f2.complete!('foo')
243
+ ff.get.should == 'foo'
244
+ end
245
+
246
+ it 'is unaffected by the completion of the other futures' do
247
+ f1 = Future.new
248
+ f2 = Future.new
249
+ f3 = Future.new
250
+ ff = Future.first(f1, f2, f3)
251
+ f2.complete!
252
+ f1.complete!
253
+ f3.complete!
254
+ end
255
+
256
+ it 'fails if all of the source futures fail' do
257
+ f1 = Future.new
258
+ f2 = Future.new
259
+ f3 = Future.new
260
+ ff = Future.first(f1, f2, f3)
261
+ f2.fail!(StandardError.new('bork'))
262
+ f1.fail!(StandardError.new('bork'))
263
+ f3.fail!(StandardError.new('bork'))
264
+ ff.should be_failed
265
+ end
266
+
267
+ it 'fails with the error of the last future to fail' do
268
+ f1 = Future.new
269
+ f2 = Future.new
270
+ f3 = Future.new
271
+ ff = Future.first(f1, f2, f3)
272
+ f2.fail!(StandardError.new('bork2'))
273
+ f1.fail!(StandardError.new('bork1'))
274
+ f3.fail!(StandardError.new('bork3'))
275
+ expect { ff.get }.to raise_error('bork3')
276
+ end
277
+ end
278
+ end
279
+
226
280
  describe '#map' do
227
281
  context 'returns a new future that' do
228
282
  it 'will complete with the result of the given block' do
@@ -271,6 +325,86 @@ module Cql
271
325
  end
272
326
  end
273
327
 
328
+ describe '#recover' do
329
+ context 'returns a new future that' do
330
+ it 'completes with a value when the source future fails' do
331
+ f1 = Future.new
332
+ f2 = f1.recover { 'foo' }
333
+ f1.fail!(StandardError.new('Bork!'))
334
+ f2.get.should == 'foo'
335
+ end
336
+
337
+ it 'yields the error to the block' do
338
+ f1 = Future.new
339
+ f2 = f1.recover { |e| e.message }
340
+ f1.fail!(StandardError.new('Bork!'))
341
+ f2.get.should == 'Bork!'
342
+ end
343
+
344
+ it 'completes with the value of the source future when the source future is successful' do
345
+ f1 = Future.new
346
+ f2 = f1.recover { 'foo' }
347
+ f1.complete!('bar')
348
+ f2.get.should == 'bar'
349
+ end
350
+
351
+ it 'fails with the error raised in the given block' do
352
+ f1 = Future.new
353
+ f2 = f1.recover { raise 'Snork!' }
354
+ f1.fail!(StandardError.new('Bork!'))
355
+ expect { f2.get }.to raise_error('Snork!')
356
+ end
357
+ end
358
+ end
359
+
360
+ describe '#fallback' do
361
+ context 'returns a new future that' do
362
+ it 'completes with the value of the fallback future when the source future fails' do
363
+ f1 = Future.new
364
+ f2 = Future.new
365
+ f3 = f1.fallback { f2 }
366
+ f1.fail!(StandardError.new('Bork!'))
367
+ f2.complete!('foo')
368
+ f3.get.should == 'foo'
369
+ end
370
+
371
+ it 'yields the error to the block' do
372
+ f1 = Future.new
373
+ f2 = Future.new
374
+ f3 = f1.fallback do |error|
375
+ Future.completed(error.message)
376
+ end
377
+ f1.fail!(StandardError.new('Bork!'))
378
+ f3.get.should == 'Bork!'
379
+ end
380
+
381
+ it 'completes with the value of the source future when the source future succeeds' do
382
+ f1 = Future.new
383
+ f2 = Future.new
384
+ f3 = f1.fallback { f2 }
385
+ f2.complete!('bar')
386
+ f1.complete!('foo')
387
+ f3.get.should == 'foo'
388
+ end
389
+
390
+ it 'fails when the block raises an error' do
391
+ f1 = Future.new
392
+ f2 = f1.fallback { raise 'Bork!' }
393
+ f1.fail!(StandardError.new('Splork!'))
394
+ expect { f2.get }.to raise_error('Bork!')
395
+ end
396
+
397
+ it 'fails when the fallback future fails' do
398
+ f1 = Future.new
399
+ f2 = Future.new
400
+ f3 = f1.fallback { f2 }
401
+ f2.fail!(StandardError.new('Bork!'))
402
+ f1.fail!(StandardError.new('Fnork!'))
403
+ expect { f3.get }.to raise_error('Bork!')
404
+ end
405
+ end
406
+ end
407
+
274
408
  describe '.completed' do
275
409
  let :future do
276
410
  described_class.completed('hello world')
@@ -41,6 +41,13 @@ module Cql
41
41
  end
42
42
  end
43
43
 
44
+ describe '#[]/#[]=' do
45
+ it 'is thread safe storage for arbitrary data' do
46
+ protocol_handler[:host_id] = 42
47
+ protocol_handler[:host_id].should == 42
48
+ end
49
+ end
50
+
44
51
  describe '#send_request' do
45
52
  before do
46
53
  connection.stub(:write).and_yield(buffer)
@@ -10,20 +10,29 @@ class FakeIoReactor
10
10
  @default_host = nil
11
11
  @connection_listeners = []
12
12
  @started_future = Cql::Future.new
13
- @startup_delay = 0
13
+ @before_startup_handler = nil
14
+ @down_nodes = []
14
15
  end
15
16
 
16
- def startup_delay=(n)
17
- @startup_delay = n
17
+ def node_down(hostname)
18
+ @down_nodes << hostname
19
+ end
20
+
21
+ def before_startup(&handler)
22
+ @before_startup_handler = handler
18
23
  end
19
24
 
20
25
  def connect(host, port, timeout)
21
- connection = FakeConnection.new(host, port, timeout)
22
- @connections << connection
23
- @connection_listeners.each do |listener|
24
- listener.call(connection)
26
+ if @down_nodes.include?(host)
27
+ Cql::Future.failed(Cql::Io::ConnectionError.new('Node down'))
28
+ else
29
+ connection = FakeConnection.new(host, port, timeout)
30
+ @connections << connection
31
+ @connection_listeners.each do |listener|
32
+ listener.call(connection)
33
+ end
34
+ Cql::Future.completed(connection)
25
35
  end
26
- Cql::Future.completed(connection)
27
36
  end
28
37
 
29
38
  def on_connection(&listener)
@@ -32,8 +41,13 @@ class FakeIoReactor
32
41
 
33
42
  def start
34
43
  @running = true
35
- Thread.start do
36
- sleep(@startup_delay)
44
+ if @before_startup_handler
45
+ @before_startup_handler = nil
46
+ Thread.start do
47
+ @before_startup_handler.call
48
+ @started_future.complete!(self)
49
+ end
50
+ elsif !(@started_future.complete? || @started_future.failed?)
37
51
  @started_future.complete!(self)
38
52
  end
39
53
  @started_future
@@ -61,14 +75,28 @@ class FakeConnection
61
75
  @responses = []
62
76
  @closed = false
63
77
  @keyspace = nil
78
+ @data = {}
79
+ @request_handler = method(:default_request_handler)
80
+ end
81
+
82
+ def [](key)
83
+ @data[key]
84
+ end
85
+
86
+ def []=(key, value)
87
+ @data[key] = value
88
+ end
89
+
90
+ def connected?
91
+ !@closed
64
92
  end
65
93
 
66
94
  def close
67
95
  @closed = true
68
96
  end
69
97
 
70
- def queue_response(response)
71
- @responses << response
98
+ def handle_request(&handler)
99
+ @request_handler = handler
72
100
  end
73
101
 
74
102
  def send_request(request)
@@ -76,11 +104,23 @@ class FakeConnection
76
104
  Cql::Future.failed(Cql::NotConnectedError.new)
77
105
  else
78
106
  @requests << request
79
- response = @responses.shift
107
+ response = @request_handler.call(request)
80
108
  if response.is_a?(Cql::Protocol::SetKeyspaceResultResponse)
81
109
  @keyspace = response.keyspace
82
110
  end
83
111
  Cql::Future.completed(response)
84
112
  end
85
113
  end
114
+
115
+ def default_request_handler(request)
116
+ response = @responses.shift
117
+ unless response
118
+ case request
119
+ when Cql::Protocol::StartupRequest
120
+ Cql::Protocol::ReadyResponse.new
121
+ when Cql::Protocol::QueryRequest
122
+ Cql::Protocol::RowsResultResponse.new([], [])
123
+ end
124
+ end
125
+ end
86
126
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cql-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.pre0
4
+ version: 1.1.0.pre1
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-04 00:00:00.000000000 Z
12
+ date: 2013-07-24 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A pure Ruby CQL3 driver for Cassandra
15
15
  email:
@@ -71,7 +71,6 @@ files:
71
71
  - spec/cql/byte_buffer_spec.rb
72
72
  - spec/cql/client/asynchronous_client_spec.rb
73
73
  - spec/cql/client/asynchronous_prepared_statement_spec.rb
74
- - spec/cql/client/client_shared.rb
75
74
  - spec/cql/client/column_metadata_spec.rb
76
75
  - spec/cql/client/request_runner_spec.rb
77
76
  - spec/cql/client/synchronous_client_spec.rb
@@ -133,7 +132,6 @@ test_files:
133
132
  - spec/cql/byte_buffer_spec.rb
134
133
  - spec/cql/client/asynchronous_client_spec.rb
135
134
  - spec/cql/client/asynchronous_prepared_statement_spec.rb
136
- - spec/cql/client/client_shared.rb
137
135
  - spec/cql/client/column_metadata_spec.rb
138
136
  - spec/cql/client/request_runner_spec.rb
139
137
  - spec/cql/client/synchronous_client_spec.rb
@@ -1,27 +0,0 @@
1
- # encoding: utf-8
2
-
3
- shared_context 'client setup' do
4
- let :connection_options do
5
- {:host => 'example.com', :port => 12321, :io_reactor => io_reactor}
6
- end
7
-
8
- let :io_reactor do
9
- FakeIoReactor.new
10
- end
11
-
12
- def connections
13
- io_reactor.connections
14
- end
15
-
16
- def last_connection
17
- connections.last
18
- end
19
-
20
- def requests
21
- last_connection.requests
22
- end
23
-
24
- def last_request
25
- requests.last
26
- end
27
- end