cql-rb 1.0.0.pre7 → 1.0.0.pre8
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 +22 -0
- data/lib/cql/client.rb +79 -310
- data/lib/cql/client/asynchronous_client.rb +254 -0
- data/lib/cql/client/asynchronous_prepared_statement.rb +19 -0
- data/lib/cql/client/column_metadata.rb +22 -0
- data/lib/cql/client/query_result.rb +34 -0
- data/lib/cql/client/result_metadata.rb +31 -0
- data/lib/cql/client/synchronous_client.rb +47 -0
- data/lib/cql/client/synchronous_prepared_statement.rb +47 -0
- data/lib/cql/future.rb +7 -3
- data/lib/cql/io.rb +1 -0
- data/lib/cql/io/io_reactor.rb +9 -3
- data/lib/cql/io/node_connection.rb +2 -2
- data/lib/cql/protocol.rb +5 -4
- data/lib/cql/protocol/request.rb +23 -0
- data/lib/cql/protocol/requests/credentials_request.rb +1 -1
- data/lib/cql/protocol/requests/execute_request.rb +13 -84
- data/lib/cql/protocol/requests/options_request.rb +1 -1
- data/lib/cql/protocol/requests/prepare_request.rb +2 -1
- data/lib/cql/protocol/requests/query_request.rb +3 -1
- data/lib/cql/protocol/requests/register_request.rb +1 -1
- data/lib/cql/protocol/requests/startup_request.rb +1 -2
- data/lib/cql/protocol/{response_body.rb → response.rb} +1 -1
- data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
- data/lib/cql/protocol/responses/error_response.rb +1 -1
- data/lib/cql/protocol/responses/ready_response.rb +1 -1
- data/lib/cql/protocol/responses/result_response.rb +1 -1
- data/lib/cql/protocol/responses/rows_result_response.rb +1 -1
- data/lib/cql/protocol/responses/supported_response.rb +1 -1
- data/lib/cql/protocol/type_converter.rb +226 -46
- data/lib/cql/version.rb +1 -1
- data/spec/cql/byte_buffer_spec.rb +38 -0
- data/spec/cql/client/asynchronous_client_spec.rb +472 -0
- data/spec/cql/client/client_shared.rb +27 -0
- data/spec/cql/client/synchronous_client_spec.rb +104 -0
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +65 -0
- data/spec/cql/future_spec.rb +4 -0
- data/spec/cql/io/io_reactor_spec.rb +39 -20
- data/spec/cql/protocol/request_spec.rb +17 -0
- data/spec/cql/protocol/requests/credentials_request_spec.rb +82 -0
- data/spec/cql/protocol/requests/execute_request_spec.rb +174 -0
- data/spec/cql/protocol/requests/options_request_spec.rb +24 -0
- data/spec/cql/protocol/requests/prepare_request_spec.rb +70 -0
- data/spec/cql/protocol/requests/query_request_spec.rb +95 -0
- data/spec/cql/protocol/requests/register_request_spec.rb +24 -0
- data/spec/cql/protocol/requests/startup_request_spec.rb +29 -0
- data/spec/integration/client_spec.rb +26 -19
- data/spec/integration/protocol_spec.rb +2 -2
- data/spec/integration/regression_spec.rb +1 -1
- metadata +35 -9
- data/lib/cql/protocol/request_body.rb +0 -15
- data/lib/cql/protocol/request_frame.rb +0 -20
- data/spec/cql/client_spec.rb +0 -454
- data/spec/cql/protocol/request_frame_spec.rb +0 -456
data/lib/cql/version.rb
CHANGED
@@ -264,6 +264,31 @@ module Cql
|
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
267
|
+
describe '#update' do
|
268
|
+
it 'changes the bytes at the specified location' do
|
269
|
+
buffer.append('foo bar')
|
270
|
+
buffer.update(4, 'baz')
|
271
|
+
buffer.to_s.should == 'foo baz'
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'handles updates after a read' do
|
275
|
+
buffer.append('foo bar')
|
276
|
+
buffer.read(1)
|
277
|
+
buffer.update(3, 'baz')
|
278
|
+
buffer.to_s.should == 'oo baz'
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'handles updates after multiple reads and appends' do
|
282
|
+
buffer.append('foo bar')
|
283
|
+
buffer.read(1)
|
284
|
+
buffer.append('x')
|
285
|
+
buffer.update(4, 'baz')
|
286
|
+
buffer.append('yyyy')
|
287
|
+
buffer.read(1)
|
288
|
+
buffer.to_s.should == 'o bbazyyyy'
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
267
292
|
describe '#dup' do
|
268
293
|
it 'returns a copy' do
|
269
294
|
buffer.append('hello world')
|
@@ -279,6 +304,19 @@ module Cql
|
|
279
304
|
end
|
280
305
|
end
|
281
306
|
|
307
|
+
describe '#cheap_peek' do
|
308
|
+
it 'returns a prefix of the buffer' do
|
309
|
+
buffer.append('foo')
|
310
|
+
buffer.append('bar')
|
311
|
+
buffer.read_byte
|
312
|
+
buffer.append('hello')
|
313
|
+
x = buffer.cheap_peek
|
314
|
+
x.bytesize.should be > 0
|
315
|
+
x.bytesize.should be <= buffer.bytesize
|
316
|
+
buffer.to_str.should start_with(x)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
282
320
|
context 'when reading and appending' do
|
283
321
|
it 'handles heavy churn' do
|
284
322
|
1000.times do
|
@@ -0,0 +1,472 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'cql/client/client_shared'
|
5
|
+
|
6
|
+
|
7
|
+
module Cql
|
8
|
+
module Client
|
9
|
+
describe AsynchronousClient do
|
10
|
+
let :client do
|
11
|
+
described_class.new(connection_options)
|
12
|
+
end
|
13
|
+
|
14
|
+
include_context 'client setup'
|
15
|
+
|
16
|
+
describe '#connect' do
|
17
|
+
it 'connects' do
|
18
|
+
client.connect.get
|
19
|
+
connections.should have(1).item
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'connects only once' do
|
23
|
+
client.connect.get
|
24
|
+
client.connect.get
|
25
|
+
connections.should have(1).item
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'connects to all hosts' do
|
29
|
+
client.close.get
|
30
|
+
io_reactor.stop.get
|
31
|
+
io_reactor.start.get
|
32
|
+
|
33
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
34
|
+
c.connect.get
|
35
|
+
connections.should have(3).items
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns itself' do
|
39
|
+
client.connect.get.should equal(client)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'forwards the host and port' do
|
43
|
+
client.connect.get
|
44
|
+
connection[:host].should == 'example.com'
|
45
|
+
connection[:port].should == 12321
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'sends a startup request' do
|
49
|
+
client.connect.get
|
50
|
+
last_request.should be_a(Protocol::StartupRequest)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'sends a startup request to each connection' do
|
54
|
+
client.close.get
|
55
|
+
io_reactor.stop.get
|
56
|
+
io_reactor.start.get
|
57
|
+
|
58
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
59
|
+
c.connect.get
|
60
|
+
connections.each do |cc|
|
61
|
+
cc[:requests].last.should be_a(Protocol::StartupRequest)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'is not in a keyspace' do
|
66
|
+
client.connect.get
|
67
|
+
client.keyspace.should be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'changes to the keyspace given as an option' do
|
71
|
+
c = described_class.new(connection_options.merge(:keyspace => 'hello_world'))
|
72
|
+
c.connect.get
|
73
|
+
last_request.should == Protocol::QueryRequest.new('USE hello_world', :one)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'validates the keyspace name before sending the USE command' do
|
77
|
+
c = described_class.new(connection_options.merge(:keyspace => 'system; DROP KEYSPACE system'))
|
78
|
+
expect { c.connect.get }.to raise_error(InvalidKeyspaceNameError)
|
79
|
+
requests.should_not include(Protocol::QueryRequest.new('USE system; DROP KEYSPACE system', :one))
|
80
|
+
end
|
81
|
+
|
82
|
+
it 're-raises any errors raised' do
|
83
|
+
io_reactor.stub(:add_connection).and_raise(ArgumentError)
|
84
|
+
expect { client.connect.get }.to raise_error(ArgumentError)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'is not connected if an error is raised' do
|
88
|
+
io_reactor.stub(:add_connection).and_raise(ArgumentError)
|
89
|
+
client.connect.get rescue nil
|
90
|
+
client.should_not be_connected
|
91
|
+
io_reactor.should_not be_running
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'is connected after #connect returns' do
|
95
|
+
client.connect.get
|
96
|
+
client.should be_connected
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'is not connected while connecting' do
|
100
|
+
pending 'the fake reactor needs to be made asynchronous' do
|
101
|
+
io_reactor.stop.get
|
102
|
+
f = client.connect
|
103
|
+
client.should_not be_connected
|
104
|
+
io_reactor.start.get
|
105
|
+
f.get
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when the server requests authentication' do
|
110
|
+
before do
|
111
|
+
io_reactor.queue_response(Protocol::AuthenticateResponse.new('com.example.Auth'))
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'sends credentials' do
|
115
|
+
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
116
|
+
client.connect.get
|
117
|
+
last_request.should == Protocol::CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'raises an error when no credentials have been given' do
|
121
|
+
client = described_class.new(connection_options)
|
122
|
+
expect { client.connect.get }.to raise_error(AuthenticationError)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'raises an error when the server responds with an error to the credentials request' do
|
126
|
+
io_reactor.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
127
|
+
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
128
|
+
expect { client.connect.get }.to raise_error(AuthenticationError)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'shuts down the client when there is an authentication error' do
|
132
|
+
io_reactor.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
133
|
+
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
134
|
+
client.connect.get rescue nil
|
135
|
+
client.should_not be_connected
|
136
|
+
io_reactor.should_not be_running
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#close' do
|
142
|
+
it 'closes the connection' do
|
143
|
+
client.connect.get
|
144
|
+
client.close.get
|
145
|
+
io_reactor.should_not be_running
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'does nothing when called before #connect' do
|
149
|
+
client.close.get
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'accepts multiple calls to #close' do
|
153
|
+
client.connect.get
|
154
|
+
client.close.get
|
155
|
+
client.close.get
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'returns itself' do
|
159
|
+
client.connect.get.close.get.should equal(client)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#use' do
|
164
|
+
before do
|
165
|
+
client.connect.get
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'executes a USE query' do
|
169
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
170
|
+
client.use('system').get
|
171
|
+
last_request.should == Protocol::QueryRequest.new('USE system', :one)
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'executes a USE query for each connection' do
|
175
|
+
client.close.get
|
176
|
+
io_reactor.stop.get
|
177
|
+
io_reactor.start.get
|
178
|
+
|
179
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
180
|
+
c.connect.get
|
181
|
+
|
182
|
+
c.use('system').get
|
183
|
+
last_requests = connections.select { |c| c[:host] =~ /^h\d\.example\.com$/ }.sort_by { |c| c[:host] }.map { |c| c[:requests].last }
|
184
|
+
last_requests.should == [
|
185
|
+
Protocol::QueryRequest.new('USE system', :one),
|
186
|
+
Protocol::QueryRequest.new('USE system', :one),
|
187
|
+
Protocol::QueryRequest.new('USE system', :one)
|
188
|
+
]
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'knows which keyspace it changed to' do
|
192
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
193
|
+
client.use('system').get
|
194
|
+
client.keyspace.should == 'system'
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'raises an error if the keyspace name is not valid' do
|
198
|
+
expect { client.use('system; DROP KEYSPACE system').get }.to raise_error(InvalidKeyspaceNameError)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#execute' do
|
203
|
+
before do
|
204
|
+
client.connect.get
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'asks the connection to execute the query' do
|
208
|
+
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3').get
|
209
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :quorum)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'uses the specified consistency' do
|
213
|
+
client.execute('UPDATE stuff SET thing = 1 WHERE id = 3', :three).get
|
214
|
+
last_request.should == Protocol::QueryRequest.new('UPDATE stuff SET thing = 1 WHERE id = 3', :three)
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'with a void CQL query' do
|
218
|
+
it 'returns nil' do
|
219
|
+
io_reactor.queue_response(Protocol::VoidResultResponse.new)
|
220
|
+
result = client.execute('UPDATE stuff SET thing = 1 WHERE id = 3').get
|
221
|
+
result.should be_nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'with a USE query' do
|
226
|
+
it 'returns nil' do
|
227
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
228
|
+
result = client.execute('USE system').get
|
229
|
+
result.should be_nil
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'knows which keyspace it changed to' do
|
233
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
234
|
+
client.execute('USE system').get
|
235
|
+
client.keyspace.should == 'system'
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'detects that one connection changed to a keyspace and changes the others too' do
|
239
|
+
client.close.get
|
240
|
+
io_reactor.stop.get
|
241
|
+
io_reactor.start.get
|
242
|
+
|
243
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
244
|
+
c.connect.get
|
245
|
+
|
246
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'), connections.find { |c| c[:host] == 'h1.example.com' }[:host])
|
247
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'), connections.find { |c| c[:host] == 'h2.example.com' }[:host])
|
248
|
+
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'), connections.find { |c| c[:host] == 'h3.example.com' }[:host])
|
249
|
+
|
250
|
+
c.execute('USE system', :one).get
|
251
|
+
c.keyspace.should == 'system'
|
252
|
+
|
253
|
+
last_requests = connections.select { |c| c[:host] =~ /^h\d\.example\.com$/ }.sort_by { |c| c[:host] }.map { |c| c[:requests].last }
|
254
|
+
last_requests.should == [
|
255
|
+
Protocol::QueryRequest.new('USE system', :one),
|
256
|
+
Protocol::QueryRequest.new('USE system', :one),
|
257
|
+
Protocol::QueryRequest.new('USE system', :one)
|
258
|
+
]
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'with an SELECT query' do
|
263
|
+
let :rows do
|
264
|
+
[['xyz', 'abc'], ['abc', 'xyz'], ['123', 'xyz']]
|
265
|
+
end
|
266
|
+
|
267
|
+
let :metadata do
|
268
|
+
[['thingies', 'things', 'thing', :text], ['thingies', 'things', 'item', :text]]
|
269
|
+
end
|
270
|
+
|
271
|
+
let :result do
|
272
|
+
io_reactor.queue_response(Protocol::RowsResultResponse.new(rows, metadata))
|
273
|
+
client.execute('SELECT * FROM things').get
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'returns an Enumerable of rows' do
|
277
|
+
row_count = 0
|
278
|
+
result.each do |row|
|
279
|
+
row_count += 1
|
280
|
+
end
|
281
|
+
row_count.should == 3
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'with metadata that' do
|
285
|
+
it 'has keyspace, table and type information' do
|
286
|
+
result.metadata['item'].keyspace.should == 'thingies'
|
287
|
+
result.metadata['item'].table.should == 'things'
|
288
|
+
result.metadata['item'].column_name.should == 'item'
|
289
|
+
result.metadata['item'].type.should == :text
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'is an Enumerable' do
|
293
|
+
result.metadata.map(&:type).should == [:text, :text]
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'is splattable' do
|
297
|
+
ks, table, col, type = result.metadata['thing']
|
298
|
+
ks.should == 'thingies'
|
299
|
+
table.should == 'things'
|
300
|
+
col.should == 'thing'
|
301
|
+
type.should == :text
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
context 'when there is an error creating the request' do
|
307
|
+
it 'returns a failed future' do
|
308
|
+
f = client.execute('SELECT * FROM stuff', :foo)
|
309
|
+
expect { f.get }.to raise_error(ArgumentError)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'when the response is an error' do
|
314
|
+
it 'raises an error' do
|
315
|
+
io_reactor.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
316
|
+
expect { client.execute('SELECT * FROM things').get }.to raise_error(QueryError, 'Blurgh')
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'decorates the error with the CQL that caused it' do
|
320
|
+
io_reactor.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
321
|
+
begin
|
322
|
+
client.execute('SELECT * FROM things').get
|
323
|
+
rescue QueryError => e
|
324
|
+
e.cql.should == 'SELECT * FROM things'
|
325
|
+
else
|
326
|
+
fail('No error was raised')
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
describe '#prepare' do
|
333
|
+
let :id do
|
334
|
+
'A' * 32
|
335
|
+
end
|
336
|
+
|
337
|
+
let :metadata do
|
338
|
+
[['stuff', 'things', 'item', :varchar]]
|
339
|
+
end
|
340
|
+
|
341
|
+
before do
|
342
|
+
client.connect.get
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'sends a prepare request' do
|
346
|
+
client.prepare('SELECT * FROM system.peers').get
|
347
|
+
last_request.should == Protocol::PrepareRequest.new('SELECT * FROM system.peers')
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'returns a prepared statement' do
|
351
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new('A' * 32, [['stuff', 'things', 'item', :varchar]]))
|
352
|
+
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
353
|
+
statement.should_not be_nil
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'executes a prepared statement' do
|
357
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
358
|
+
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
359
|
+
statement.execute('foo').get
|
360
|
+
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], :quorum)
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'returns a prepared statement that knows the metadata' do
|
364
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
365
|
+
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
366
|
+
statement.metadata['item'].type == :varchar
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'executes a prepared statement with a specific consistency level' do
|
370
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
371
|
+
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
372
|
+
statement.execute('thing', :local_quorum).get
|
373
|
+
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], :local_quorum)
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'executes a prepared statement using the right connection' do
|
377
|
+
client.close.get
|
378
|
+
io_reactor.stop.get
|
379
|
+
io_reactor.start.get
|
380
|
+
|
381
|
+
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
382
|
+
c.connect.get
|
383
|
+
|
384
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new('A' * 32, metadata))
|
385
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new('B' * 32, metadata))
|
386
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new('C' * 32, metadata))
|
387
|
+
|
388
|
+
statement1 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
389
|
+
statement1_connection = io_reactor.last_used_connection
|
390
|
+
statement2 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
391
|
+
statement2_connection = io_reactor.last_used_connection
|
392
|
+
statement3 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
393
|
+
statement3_connection = io_reactor.last_used_connection
|
394
|
+
|
395
|
+
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo1'}], metadata), statement1_connection[:host])
|
396
|
+
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo2'}], metadata), statement2_connection[:host])
|
397
|
+
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo3'}], metadata), statement3_connection[:host])
|
398
|
+
|
399
|
+
statement1.execute('foo').get.first.should == {'thing' => 'foo1'}
|
400
|
+
statement2.execute('foo').get.first.should == {'thing' => 'foo2'}
|
401
|
+
statement3.execute('foo').get.first.should == {'thing' => 'foo3'}
|
402
|
+
end
|
403
|
+
|
404
|
+
context 'when there is an error creating the request' do
|
405
|
+
it 'returns a failed future' do
|
406
|
+
f = client.prepare(nil)
|
407
|
+
expect { f.get }.to raise_error(ArgumentError)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context 'when there is an error preparing the request' do
|
412
|
+
it 'returns a failed future' do
|
413
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
414
|
+
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
415
|
+
f = statement.execute
|
416
|
+
expect { f.get }.to raise_error(ArgumentError)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
context 'when not connected' do
|
422
|
+
it 'is not connected before #connect has been called' do
|
423
|
+
client.should_not be_connected
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'is not connected after #close has been called' do
|
427
|
+
client.connect.get
|
428
|
+
client.close.get
|
429
|
+
client.should_not be_connected
|
430
|
+
end
|
431
|
+
|
432
|
+
it 'complains when #use is called before #connect' do
|
433
|
+
expect { client.use('system').get }.to raise_error(NotConnectedError)
|
434
|
+
end
|
435
|
+
|
436
|
+
it 'complains when #use is called after #close' do
|
437
|
+
client.connect.get
|
438
|
+
client.close.get
|
439
|
+
expect { client.use('system').get }.to raise_error(NotConnectedError)
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'complains when #execute is called before #connect' do
|
443
|
+
expect { client.execute('DELETE FROM stuff WHERE id = 3').get }.to raise_error(NotConnectedError)
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'complains when #execute is called after #close' do
|
447
|
+
client.connect.get
|
448
|
+
client.close.get
|
449
|
+
expect { client.execute('DELETE FROM stuff WHERE id = 3').get }.to raise_error(NotConnectedError)
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'complains when #prepare is called before #connect' do
|
453
|
+
expect { client.prepare('DELETE FROM stuff WHERE id = 3').get }.to raise_error(NotConnectedError)
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'complains when #prepare is called after #close' do
|
457
|
+
client.connect.get
|
458
|
+
client.close.get
|
459
|
+
expect { client.prepare('DELETE FROM stuff WHERE id = 3').get }.to raise_error(NotConnectedError)
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'complains when #execute of a prepared statement is called after #close' do
|
463
|
+
client.connect.get
|
464
|
+
io_reactor.queue_response(Protocol::PreparedResultResponse.new('A' * 32, []))
|
465
|
+
statement = client.prepare('DELETE FROM stuff WHERE id = 3').get
|
466
|
+
client.close.get
|
467
|
+
expect { statement.execute.get }.to raise_error(NotConnectedError)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
end
|