cql-rb 1.1.0.pre0 → 1.1.0.pre1
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.
- data/lib/cql/byte_buffer.rb +1 -0
- data/lib/cql/client.rb +20 -8
- data/lib/cql/client/asynchronous_client.rb +128 -43
- data/lib/cql/client/synchronous_client.rb +25 -5
- data/lib/cql/client/synchronous_prepared_statement.rb +4 -2
- data/lib/cql/future.rb +97 -0
- data/lib/cql/io/connection.rb +1 -0
- data/lib/cql/io/io_reactor.rb +2 -0
- data/lib/cql/protocol/cql_protocol_handler.rb +16 -1
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +286 -52
- data/spec/cql/client/synchronous_client_spec.rb +24 -1
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +24 -0
- data/spec/cql/future_spec.rb +134 -0
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +7 -0
- data/spec/support/fake_io_reactor.rb +53 -13
- metadata +2 -4
- data/spec/cql/client/client_shared.rb +0 -27
data/lib/cql/io/connection.rb
CHANGED
data/lib/cql/io/io_reactor.rb
CHANGED
@@ -10,7 +10,7 @@ module Cql
|
|
10
10
|
#
|
11
11
|
# Instances of this class are thread safe.
|
12
12
|
#
|
13
|
-
# @
|
13
|
+
# @example Sending an OPTIONS request
|
14
14
|
# future = protocol_handler.send_request(Cql::Protocol::OptionsRequest.new)
|
15
15
|
# response = future.get
|
16
16
|
# puts "These options are supported: #{response.options}"
|
@@ -29,11 +29,26 @@ module Cql
|
|
29
29
|
@request_queue_in = []
|
30
30
|
@request_queue_out = []
|
31
31
|
@event_listeners = []
|
32
|
+
@data = {}
|
32
33
|
@lock = Mutex.new
|
33
34
|
@closed_future = Future.new
|
34
35
|
@keyspace = nil
|
35
36
|
end
|
36
37
|
|
38
|
+
# Associate arbitrary data with this protocol handler object. This is
|
39
|
+
# useful in situations where additional metadata can be loaded after the
|
40
|
+
# connection has been set up, or to keep statistics specific to the
|
41
|
+
# connection this protocol handler wraps.
|
42
|
+
def []=(key, value)
|
43
|
+
@lock.synchronize { @data[key] = value }
|
44
|
+
end
|
45
|
+
|
46
|
+
# @see {#[]=}
|
47
|
+
# @return the value associated with the key
|
48
|
+
def [](key)
|
49
|
+
@lock.synchronize { @data[key] }
|
50
|
+
end
|
51
|
+
|
37
52
|
# @return [true, false] true if the underlying connection is connected
|
38
53
|
def connected?
|
39
54
|
@connection.connected?
|
data/lib/cql/version.rb
CHANGED
@@ -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
|
@@ -11,7 +10,48 @@ module Cql
|
|
11
10
|
described_class.new(connection_options)
|
12
11
|
end
|
13
12
|
|
14
|
-
|
13
|
+
let :connection_options do
|
14
|
+
{:host => 'example.com', :port => 12321, :io_reactor => io_reactor}
|
15
|
+
end
|
16
|
+
|
17
|
+
let :io_reactor do
|
18
|
+
FakeIoReactor.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def connections
|
22
|
+
io_reactor.connections
|
23
|
+
end
|
24
|
+
|
25
|
+
def last_connection
|
26
|
+
connections.last
|
27
|
+
end
|
28
|
+
|
29
|
+
def requests
|
30
|
+
last_connection.requests
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_request
|
34
|
+
requests.last
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_request(&handler)
|
38
|
+
@request_handler = handler
|
39
|
+
end
|
40
|
+
|
41
|
+
before do
|
42
|
+
io_reactor.on_connection do |connection|
|
43
|
+
connection.handle_request do |request|
|
44
|
+
response = nil
|
45
|
+
if @request_handler
|
46
|
+
response = @request_handler.call(request, connection, proc { connection.default_request_handler(request) })
|
47
|
+
end
|
48
|
+
unless response
|
49
|
+
response = connection.default_request_handler(request)
|
50
|
+
end
|
51
|
+
response
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
15
55
|
|
16
56
|
describe '#connect' do
|
17
57
|
it 'connects' do
|
@@ -25,14 +65,45 @@ module Cql
|
|
25
65
|
connections.should have(1).item
|
26
66
|
end
|
27
67
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
68
|
+
context 'when connecting to multiple hosts' do
|
69
|
+
before do
|
70
|
+
client.close.get
|
71
|
+
io_reactor.stop.get
|
72
|
+
end
|
32
73
|
|
33
|
-
|
34
|
-
|
35
|
-
|
74
|
+
it 'connects to all hosts' do
|
75
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h3.example.com]))
|
76
|
+
c.connect.get
|
77
|
+
connections.should have(3).items
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'connects to all hosts, when given as a comma-sepatated string' do
|
81
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
82
|
+
c.connect.get
|
83
|
+
connections.should have(3).items
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'only connects to each host once' do
|
87
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h2.example.com]))
|
88
|
+
c.connect.get
|
89
|
+
connections.should have(2).items
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'succeeds even if only one of the connections succeeded' do
|
93
|
+
io_reactor.node_down('h1.example.com')
|
94
|
+
io_reactor.node_down('h3.example.com')
|
95
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h2.example.com]))
|
96
|
+
c.connect.get
|
97
|
+
connections.should have(1).items
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'fails when all nodes are down' do
|
101
|
+
io_reactor.node_down('h1.example.com')
|
102
|
+
io_reactor.node_down('h2.example.com')
|
103
|
+
io_reactor.node_down('h3.example.com')
|
104
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h2.example.com]))
|
105
|
+
expect { c.connect.get }.to raise_error(Io::ConnectionError)
|
106
|
+
end
|
36
107
|
end
|
37
108
|
|
38
109
|
it 'returns itself' do
|
@@ -52,7 +123,7 @@ module Cql
|
|
52
123
|
|
53
124
|
it 'sends a startup request' do
|
54
125
|
client.connect.get
|
55
|
-
|
126
|
+
requests.first.should be_a(Protocol::StartupRequest)
|
56
127
|
end
|
57
128
|
|
58
129
|
it 'sends a startup request to each connection' do
|
@@ -60,10 +131,10 @@ module Cql
|
|
60
131
|
io_reactor.stop.get
|
61
132
|
io_reactor.start.get
|
62
133
|
|
63
|
-
c = described_class.new(connection_options.merge(
|
134
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h3.example.com]))
|
64
135
|
c.connect.get
|
65
136
|
connections.each do |cc|
|
66
|
-
cc.requests.
|
137
|
+
cc.requests.first.should be_a(Protocol::StartupRequest)
|
67
138
|
end
|
68
139
|
end
|
69
140
|
|
@@ -75,7 +146,8 @@ module Cql
|
|
75
146
|
it 'changes to the keyspace given as an option' do
|
76
147
|
c = described_class.new(connection_options.merge(:keyspace => 'hello_world'))
|
77
148
|
c.connect.get
|
78
|
-
|
149
|
+
request = requests.find { |rq| rq == Protocol::QueryRequest.new('USE hello_world', :one) }
|
150
|
+
request.should_not be_nil, 'expected a USE request to have been sent'
|
79
151
|
end
|
80
152
|
|
81
153
|
it 'validates the keyspace name before sending the USE command' do
|
@@ -84,6 +156,107 @@ module Cql
|
|
84
156
|
requests.should_not include(Protocol::QueryRequest.new('USE system; DROP KEYSPACE system', :one))
|
85
157
|
end
|
86
158
|
|
159
|
+
context 'with automatic peer discovery' do
|
160
|
+
let :local_info do
|
161
|
+
{
|
162
|
+
'data_center' => 'dc1',
|
163
|
+
'host_id' => nil,
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
let :local_metadata do
|
168
|
+
[
|
169
|
+
['system', 'local', 'data_center', :text],
|
170
|
+
['system', 'local', 'host_id', :uuid],
|
171
|
+
]
|
172
|
+
end
|
173
|
+
|
174
|
+
let :peer_metadata do
|
175
|
+
[
|
176
|
+
['system', 'peers', 'peer', :inet],
|
177
|
+
['system', 'peers', 'data_center', :varchar],
|
178
|
+
['system', 'peers', 'host_id', :uuid],
|
179
|
+
['system', 'peers', 'rpc_address', :inet],
|
180
|
+
]
|
181
|
+
end
|
182
|
+
|
183
|
+
let :data_centers do
|
184
|
+
Hash.new('dc1')
|
185
|
+
end
|
186
|
+
|
187
|
+
let :additional_nodes do
|
188
|
+
Array.new(5) { IPAddr.new("127.0.#{rand(255)}.#{rand(255)}") }
|
189
|
+
end
|
190
|
+
|
191
|
+
before do
|
192
|
+
uuid_generator = TimeUuid::Generator.new
|
193
|
+
additional_rpc_addresses = additional_nodes.dup
|
194
|
+
io_reactor.on_connection do |connection|
|
195
|
+
connection[:spec_host_id] = uuid_generator.next
|
196
|
+
connection[:spec_data_center] = data_centers[connection.host]
|
197
|
+
connection.handle_request do |request|
|
198
|
+
case request
|
199
|
+
when Protocol::StartupRequest
|
200
|
+
Protocol::ReadyResponse.new
|
201
|
+
when Protocol::QueryRequest
|
202
|
+
case request.cql
|
203
|
+
when /FROM system\.local/
|
204
|
+
row = {'host_id' => connection[:spec_host_id], 'data_center' => connection[:spec_data_center]}
|
205
|
+
Protocol::RowsResultResponse.new([row], local_metadata)
|
206
|
+
when /FROM system\.peers/
|
207
|
+
other_host_ids = connections.reject { |c| c[:spec_host_id] == connection[:spec_host_id] }.map { |c| c[:spec_host_id] }
|
208
|
+
until other_host_ids.size >= 2
|
209
|
+
other_host_ids << uuid_generator.next
|
210
|
+
end
|
211
|
+
rows = other_host_ids.map do |host_id|
|
212
|
+
ip = additional_rpc_addresses.shift
|
213
|
+
{'host_id' => host_id, 'data_center' => data_centers[ip], 'rpc_address' => ip}
|
214
|
+
end
|
215
|
+
Protocol::RowsResultResponse.new(rows, peer_metadata)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'connects to the other nodes in the cluster' do
|
223
|
+
client.connect.get
|
224
|
+
connections.should have(3).items
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'connects to the other nodes in the same data center' do
|
228
|
+
data_centers[additional_nodes[1]] = 'dc2'
|
229
|
+
client.connect.get
|
230
|
+
connections.should have(2).items
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'connects to the other nodes in same data centers as the seed nodes' do
|
234
|
+
data_centers['host2'] = 'dc2'
|
235
|
+
data_centers[additional_nodes[1]] = 'dc2'
|
236
|
+
c = described_class.new(connection_options.merge(hosts: %w[host1 host2]))
|
237
|
+
c.connect.get
|
238
|
+
connections.should have(3).items
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'only connects to the other nodes in the cluster it is not already connected do' do
|
242
|
+
c = described_class.new(connection_options.merge(hosts: %w[host1 host2]))
|
243
|
+
c.connect.get
|
244
|
+
connections.should have(3).items
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'handles the case when it is already connected to all nodes' do
|
248
|
+
c = described_class.new(connection_options.merge(hosts: %w[host1 host2 host3 host4]))
|
249
|
+
c.connect.get
|
250
|
+
connections.should have(4).items
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'accepts that some nodes are down' do
|
254
|
+
io_reactor.node_down(additional_nodes.first.to_s)
|
255
|
+
client.connect.get
|
256
|
+
connections.should have(2).items
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
87
260
|
it 're-raises any errors raised' do
|
88
261
|
io_reactor.stub(:connect).and_raise(ArgumentError)
|
89
262
|
expect { client.connect.get }.to raise_error(ArgumentError)
|
@@ -102,24 +275,45 @@ module Cql
|
|
102
275
|
end
|
103
276
|
|
104
277
|
it 'is not connected while connecting' do
|
278
|
+
go = false
|
105
279
|
io_reactor.stop.get
|
106
|
-
|
107
|
-
client.
|
108
|
-
|
109
|
-
|
280
|
+
io_reactor.before_startup { sleep 0.01 until go }
|
281
|
+
client.connect
|
282
|
+
begin
|
283
|
+
client.should_not be_connected
|
284
|
+
ensure
|
285
|
+
go = true
|
286
|
+
end
|
110
287
|
end
|
111
288
|
|
112
289
|
context 'when the server requests authentication' do
|
113
|
-
|
114
|
-
|
115
|
-
|
290
|
+
def accepting_request_handler(request, *)
|
291
|
+
case request
|
292
|
+
when Protocol::StartupRequest
|
293
|
+
Protocol::AuthenticateResponse.new('com.example.Auth')
|
294
|
+
when Protocol::CredentialsRequest
|
295
|
+
Protocol::ReadyResponse.new
|
116
296
|
end
|
117
297
|
end
|
118
298
|
|
299
|
+
def denying_request_handler(request, *)
|
300
|
+
case request
|
301
|
+
when Protocol::StartupRequest
|
302
|
+
Protocol::AuthenticateResponse.new('com.example.Auth')
|
303
|
+
when Protocol::CredentialsRequest
|
304
|
+
Protocol::ErrorResponse.new(256, 'No way, José')
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
before do
|
309
|
+
handle_request(&method(:accepting_request_handler))
|
310
|
+
end
|
311
|
+
|
119
312
|
it 'sends credentials' do
|
120
313
|
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
121
314
|
client.connect.get
|
122
|
-
|
315
|
+
request = requests.find { |rq| rq == Protocol::CredentialsRequest.new('username' => 'foo', 'password' => 'bar') }
|
316
|
+
request.should_not be_nil, 'expected a credentials request to have been sent'
|
123
317
|
end
|
124
318
|
|
125
319
|
it 'raises an error when no credentials have been given' do
|
@@ -128,17 +322,13 @@ module Cql
|
|
128
322
|
end
|
129
323
|
|
130
324
|
it 'raises an error when the server responds with an error to the credentials request' do
|
131
|
-
|
132
|
-
connection.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
133
|
-
end
|
325
|
+
handle_request(&method(:denying_request_handler))
|
134
326
|
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
135
327
|
expect { client.connect.get }.to raise_error(AuthenticationError)
|
136
328
|
end
|
137
329
|
|
138
330
|
it 'shuts down the client when there is an authentication error' do
|
139
|
-
|
140
|
-
connection.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
141
|
-
end
|
331
|
+
handle_request(&method(:denying_request_handler))
|
142
332
|
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
143
333
|
client.connect.get rescue nil
|
144
334
|
client.should_not be_connected
|
@@ -175,14 +365,13 @@ module Cql
|
|
175
365
|
end
|
176
366
|
|
177
367
|
describe '#use' do
|
178
|
-
before do
|
179
|
-
client.connect.get
|
180
|
-
end
|
181
|
-
|
182
368
|
it 'executes a USE query' do
|
183
|
-
|
184
|
-
|
369
|
+
handle_request do |request|
|
370
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE system'
|
371
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
372
|
+
end
|
185
373
|
end
|
374
|
+
client.connect.get
|
186
375
|
client.use('system').get
|
187
376
|
last_request.should == Protocol::QueryRequest.new('USE system', :one)
|
188
377
|
end
|
@@ -192,7 +381,7 @@ module Cql
|
|
192
381
|
io_reactor.stop.get
|
193
382
|
io_reactor.start.get
|
194
383
|
|
195
|
-
c = described_class.new(connection_options.merge(
|
384
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h3.example.com]))
|
196
385
|
c.connect.get
|
197
386
|
|
198
387
|
c.use('system').get
|
@@ -205,12 +394,18 @@ module Cql
|
|
205
394
|
end
|
206
395
|
|
207
396
|
it 'knows which keyspace it changed to' do
|
208
|
-
|
397
|
+
handle_request do |request|
|
398
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE system'
|
399
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
400
|
+
end
|
401
|
+
end
|
402
|
+
client.connect.get
|
209
403
|
client.use('system').get
|
210
404
|
client.keyspace.should == 'system'
|
211
405
|
end
|
212
406
|
|
213
407
|
it 'raises an error if the keyspace name is not valid' do
|
408
|
+
client.connect.get
|
214
409
|
expect { client.use('system; DROP KEYSPACE system').get }.to raise_error(InvalidKeyspaceNameError)
|
215
410
|
end
|
216
411
|
end
|
@@ -232,7 +427,11 @@ module Cql
|
|
232
427
|
|
233
428
|
context 'with a void CQL query' do
|
234
429
|
it 'returns nil' do
|
235
|
-
|
430
|
+
handle_request do |request|
|
431
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql =~ /UPDATE/
|
432
|
+
Protocol::VoidResultResponse.new
|
433
|
+
end
|
434
|
+
end
|
236
435
|
result = client.execute('UPDATE stuff SET thing = 1 WHERE id = 3').get
|
237
436
|
result.should be_nil
|
238
437
|
end
|
@@ -240,13 +439,21 @@ module Cql
|
|
240
439
|
|
241
440
|
context 'with a USE query' do
|
242
441
|
it 'returns nil' do
|
243
|
-
|
442
|
+
handle_request do |request|
|
443
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE system'
|
444
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
445
|
+
end
|
446
|
+
end
|
244
447
|
result = client.execute('USE system').get
|
245
448
|
result.should be_nil
|
246
449
|
end
|
247
450
|
|
248
451
|
it 'knows which keyspace it changed to' do
|
249
|
-
|
452
|
+
handle_request do |request|
|
453
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE system'
|
454
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
455
|
+
end
|
456
|
+
end
|
250
457
|
client.execute('USE system').get
|
251
458
|
client.keyspace.should == 'system'
|
252
459
|
end
|
@@ -256,12 +463,14 @@ module Cql
|
|
256
463
|
io_reactor.stop.get
|
257
464
|
io_reactor.start.get
|
258
465
|
|
259
|
-
|
260
|
-
|
466
|
+
handle_request do |request, connection|
|
467
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql == 'USE system'
|
468
|
+
Protocol::SetKeyspaceResultResponse.new('system')
|
469
|
+
end
|
470
|
+
end
|
261
471
|
|
262
|
-
|
263
|
-
|
264
|
-
connections.find { |c| c.host == 'h3.example.com' }.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
472
|
+
c = described_class.new(connection_options.merge(hosts: %w[h1.example.com h2.example.com h3.example.com]))
|
473
|
+
c.connect.get
|
265
474
|
|
266
475
|
c.execute('USE system', :one).get
|
267
476
|
c.keyspace.should == 'system'
|
@@ -285,10 +494,17 @@ module Cql
|
|
285
494
|
end
|
286
495
|
|
287
496
|
let :result do
|
288
|
-
last_connection.queue_response(Protocol::RowsResultResponse.new(rows, metadata))
|
289
497
|
client.execute('SELECT * FROM things').get
|
290
498
|
end
|
291
499
|
|
500
|
+
before do
|
501
|
+
handle_request do |request|
|
502
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql =~ /FROM things/
|
503
|
+
Protocol::RowsResultResponse.new(rows, metadata)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
292
508
|
it 'returns an Enumerable of rows' do
|
293
509
|
row_count = 0
|
294
510
|
result.each do |row|
|
@@ -327,13 +543,19 @@ module Cql
|
|
327
543
|
end
|
328
544
|
|
329
545
|
context 'when the response is an error' do
|
546
|
+
before do
|
547
|
+
handle_request do |request|
|
548
|
+
if request.is_a?(Protocol::QueryRequest) && request.cql =~ /FROM things/
|
549
|
+
Protocol::ErrorResponse.new(0xabcd, 'Blurgh')
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
330
554
|
it 'raises an error' do
|
331
|
-
last_connection.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
332
555
|
expect { client.execute('SELECT * FROM things').get }.to raise_error(QueryError, 'Blurgh')
|
333
556
|
end
|
334
557
|
|
335
558
|
it 'decorates the error with the CQL that caused it' do
|
336
|
-
last_connection.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
337
559
|
begin
|
338
560
|
client.execute('SELECT * FROM things').get
|
339
561
|
rescue QueryError => e
|
@@ -354,6 +576,14 @@ module Cql
|
|
354
576
|
[['stuff', 'things', 'item', :varchar]]
|
355
577
|
end
|
356
578
|
|
579
|
+
before do
|
580
|
+
handle_request do |request|
|
581
|
+
if request.is_a?(Protocol::PrepareRequest)
|
582
|
+
Protocol::PreparedResultResponse.new(id, metadata)
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
357
587
|
before do
|
358
588
|
client.connect.get
|
359
589
|
end
|
@@ -364,26 +594,22 @@ module Cql
|
|
364
594
|
end
|
365
595
|
|
366
596
|
it 'returns a prepared statement' do
|
367
|
-
last_connection.queue_response(Protocol::PreparedResultResponse.new('A' * 32, [['stuff', 'things', 'item', :varchar]]))
|
368
597
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
369
598
|
statement.should_not be_nil
|
370
599
|
end
|
371
600
|
|
372
601
|
it 'executes a prepared statement' do
|
373
|
-
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
374
602
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
375
603
|
statement.execute('foo').get
|
376
604
|
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], :quorum)
|
377
605
|
end
|
378
606
|
|
379
607
|
it 'returns a prepared statement that knows the metadata' do
|
380
|
-
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
381
608
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
382
609
|
statement.metadata['item'].type == :varchar
|
383
610
|
end
|
384
611
|
|
385
612
|
it 'executes a prepared statement with a specific consistency level' do
|
386
|
-
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
387
613
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
388
614
|
statement.execute('thing', :local_quorum).get
|
389
615
|
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], :local_quorum)
|
@@ -398,7 +624,11 @@ module Cql
|
|
398
624
|
|
399
625
|
context 'when there is an error preparing the request' do
|
400
626
|
it 'returns a failed future' do
|
401
|
-
|
627
|
+
handle_request do |request|
|
628
|
+
if request.is_a?(Protocol::PrepareRequest)
|
629
|
+
Protocol::PreparedResultResponse.new(id, metadata)
|
630
|
+
end
|
631
|
+
end
|
402
632
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
403
633
|
f = statement.execute
|
404
634
|
expect { f.get }.to raise_error(ArgumentError)
|
@@ -448,8 +678,12 @@ module Cql
|
|
448
678
|
end
|
449
679
|
|
450
680
|
it 'complains when #execute of a prepared statement is called after #close' do
|
681
|
+
handle_request do |request|
|
682
|
+
if request.is_a?(Protocol::PrepareRequest)
|
683
|
+
Protocol::PreparedResultResponse.new('A' * 32, [])
|
684
|
+
end
|
685
|
+
end
|
451
686
|
client.connect.get
|
452
|
-
last_connection.queue_response(Protocol::PreparedResultResponse.new('A' * 32, []))
|
453
687
|
statement = client.prepare('DELETE FROM stuff WHERE id = 3').get
|
454
688
|
client.close.get
|
455
689
|
expect { statement.execute.get }.to raise_error(NotConnectedError)
|