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.
Files changed (54) hide show
  1. data/lib/cql/byte_buffer.rb +22 -0
  2. data/lib/cql/client.rb +79 -310
  3. data/lib/cql/client/asynchronous_client.rb +254 -0
  4. data/lib/cql/client/asynchronous_prepared_statement.rb +19 -0
  5. data/lib/cql/client/column_metadata.rb +22 -0
  6. data/lib/cql/client/query_result.rb +34 -0
  7. data/lib/cql/client/result_metadata.rb +31 -0
  8. data/lib/cql/client/synchronous_client.rb +47 -0
  9. data/lib/cql/client/synchronous_prepared_statement.rb +47 -0
  10. data/lib/cql/future.rb +7 -3
  11. data/lib/cql/io.rb +1 -0
  12. data/lib/cql/io/io_reactor.rb +9 -3
  13. data/lib/cql/io/node_connection.rb +2 -2
  14. data/lib/cql/protocol.rb +5 -4
  15. data/lib/cql/protocol/request.rb +23 -0
  16. data/lib/cql/protocol/requests/credentials_request.rb +1 -1
  17. data/lib/cql/protocol/requests/execute_request.rb +13 -84
  18. data/lib/cql/protocol/requests/options_request.rb +1 -1
  19. data/lib/cql/protocol/requests/prepare_request.rb +2 -1
  20. data/lib/cql/protocol/requests/query_request.rb +3 -1
  21. data/lib/cql/protocol/requests/register_request.rb +1 -1
  22. data/lib/cql/protocol/requests/startup_request.rb +1 -2
  23. data/lib/cql/protocol/{response_body.rb → response.rb} +1 -1
  24. data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
  25. data/lib/cql/protocol/responses/error_response.rb +1 -1
  26. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  27. data/lib/cql/protocol/responses/result_response.rb +1 -1
  28. data/lib/cql/protocol/responses/rows_result_response.rb +1 -1
  29. data/lib/cql/protocol/responses/supported_response.rb +1 -1
  30. data/lib/cql/protocol/type_converter.rb +226 -46
  31. data/lib/cql/version.rb +1 -1
  32. data/spec/cql/byte_buffer_spec.rb +38 -0
  33. data/spec/cql/client/asynchronous_client_spec.rb +472 -0
  34. data/spec/cql/client/client_shared.rb +27 -0
  35. data/spec/cql/client/synchronous_client_spec.rb +104 -0
  36. data/spec/cql/client/synchronous_prepared_statement_spec.rb +65 -0
  37. data/spec/cql/future_spec.rb +4 -0
  38. data/spec/cql/io/io_reactor_spec.rb +39 -20
  39. data/spec/cql/protocol/request_spec.rb +17 -0
  40. data/spec/cql/protocol/requests/credentials_request_spec.rb +82 -0
  41. data/spec/cql/protocol/requests/execute_request_spec.rb +174 -0
  42. data/spec/cql/protocol/requests/options_request_spec.rb +24 -0
  43. data/spec/cql/protocol/requests/prepare_request_spec.rb +70 -0
  44. data/spec/cql/protocol/requests/query_request_spec.rb +95 -0
  45. data/spec/cql/protocol/requests/register_request_spec.rb +24 -0
  46. data/spec/cql/protocol/requests/startup_request_spec.rb +29 -0
  47. data/spec/integration/client_spec.rb +26 -19
  48. data/spec/integration/protocol_spec.rb +2 -2
  49. data/spec/integration/regression_spec.rb +1 -1
  50. metadata +35 -9
  51. data/lib/cql/protocol/request_body.rb +0 -15
  52. data/lib/cql/protocol/request_frame.rb +0 -20
  53. data/spec/cql/client_spec.rb +0 -454
  54. data/spec/cql/protocol/request_frame_spec.rb +0 -456
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Cql
4
- VERSION = '1.0.0.pre7'.freeze
4
+ VERSION = '1.0.0.pre8'.freeze
5
5
  end
@@ -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