cql-rb 1.0.6 → 1.1.0.pre0
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/README.md +4 -9
- data/lib/cql.rb +1 -0
- data/lib/cql/byte_buffer.rb +23 -7
- data/lib/cql/client.rb +11 -6
- data/lib/cql/client/asynchronous_client.rb +37 -83
- data/lib/cql/client/asynchronous_prepared_statement.rb +10 -4
- data/lib/cql/client/column_metadata.rb +16 -0
- data/lib/cql/client/request_runner.rb +46 -0
- data/lib/cql/future.rb +4 -5
- data/lib/cql/io.rb +2 -5
- data/lib/cql/io/connection.rb +220 -0
- data/lib/cql/io/io_reactor.rb +213 -185
- data/lib/cql/protocol.rb +1 -0
- data/lib/cql/protocol/cql_protocol_handler.rb +201 -0
- data/lib/cql/protocol/decoding.rb +6 -31
- data/lib/cql/protocol/encoding.rb +1 -5
- data/lib/cql/protocol/request.rb +4 -0
- data/lib/cql/protocol/responses/schema_change_result_response.rb +15 -0
- data/lib/cql/protocol/type_converter.rb +56 -76
- data/lib/cql/time_uuid.rb +104 -0
- data/lib/cql/uuid.rb +4 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +47 -71
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +68 -0
- data/spec/cql/client/client_shared.rb +3 -3
- data/spec/cql/client/column_metadata_spec.rb +80 -0
- data/spec/cql/client/request_runner_spec.rb +120 -0
- data/spec/cql/future_spec.rb +26 -11
- data/spec/cql/io/connection_spec.rb +460 -0
- data/spec/cql/io/io_reactor_spec.rb +212 -265
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +216 -0
- data/spec/cql/protocol/decoding_spec.rb +9 -28
- data/spec/cql/protocol/encoding_spec.rb +0 -5
- data/spec/cql/protocol/request_spec.rb +16 -0
- data/spec/cql/protocol/response_frame_spec.rb +2 -2
- data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +70 -0
- data/spec/cql/time_uuid_spec.rb +136 -0
- data/spec/cql/uuid_spec.rb +1 -5
- data/spec/integration/client_spec.rb +34 -38
- data/spec/integration/io_spec.rb +283 -0
- data/spec/integration/protocol_spec.rb +53 -113
- data/spec/integration/regression_spec.rb +124 -0
- data/spec/integration/uuid_spec.rb +76 -0
- data/spec/spec_helper.rb +12 -9
- data/spec/support/fake_io_reactor.rb +52 -21
- data/spec/support/fake_server.rb +2 -2
- metadata +33 -10
- checksums.yaml +0 -15
- data/lib/cql/io/node_connection.rb +0 -209
- data/spec/cql/protocol/type_converter_spec.rb +0 -52
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cql
|
4
|
+
# A variant of UUID which can extract its time component.
|
5
|
+
#
|
6
|
+
class TimeUuid < Uuid
|
7
|
+
# Returns the time component from this UUID as a Time.
|
8
|
+
#
|
9
|
+
# @return [Time]
|
10
|
+
#
|
11
|
+
def to_time
|
12
|
+
n = (value >> 64)
|
13
|
+
t = 0
|
14
|
+
t |= (n & 0x0000000000000fff) << 48
|
15
|
+
t |= (n & 0x00000000ffff0000) << 16
|
16
|
+
t |= (n & 0xffffffff00000000) >> 32
|
17
|
+
t -= GREGORIAN_OFFSET
|
18
|
+
seconds = t/10_000_000
|
19
|
+
microseconds = (t - seconds * 10_000_000)/10.0
|
20
|
+
Time.at(seconds, microseconds).utc
|
21
|
+
end
|
22
|
+
|
23
|
+
# A UUID version 1, variant 1 generator. It can generate a sequence of UUIDs
|
24
|
+
# with reasonable uniqueness guarantees:
|
25
|
+
#
|
26
|
+
# * The clock ID and node ID components are set to random numbers when the
|
27
|
+
# generator is created.
|
28
|
+
# * If two calls to {#next} happen within the time afforded by the system
|
29
|
+
# clock resolution a counter is incremented and added to the time
|
30
|
+
# component.
|
31
|
+
# * If the clock moves backwards the clock ID is reset to a new random
|
32
|
+
# number.
|
33
|
+
#
|
34
|
+
# Instances of this class are absolutely not threadsafe. You should
|
35
|
+
# never share instances between threads.
|
36
|
+
#
|
37
|
+
class Generator
|
38
|
+
# Create a new UUID generator.
|
39
|
+
#
|
40
|
+
# @param [Integer] node_id an alternate node ID (defaults to a random number)
|
41
|
+
# @param [Integer] clock_id an alternate clock ID (defaults to a random number)
|
42
|
+
#
|
43
|
+
def initialize(node_id=nil, clock_id=nil, clock=Time)
|
44
|
+
@node_id = node_id || (rand(2**47) | 0x010000000000)
|
45
|
+
@clock_id = clock_id || rand(2**16)
|
46
|
+
@clock = clock
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a new UUID with a time component that is the current time.
|
50
|
+
#
|
51
|
+
# @return [Cql::TimeUuid] a new UUID
|
52
|
+
#
|
53
|
+
def next
|
54
|
+
now = @clock.now
|
55
|
+
usecs = now.to_i * 1_000_000 + now.usec
|
56
|
+
if @last_usecs && @last_usecs - @sequence <= usecs && usecs <= @last_usecs
|
57
|
+
@sequence += 1
|
58
|
+
elsif @last_usecs && @last_usecs > usecs
|
59
|
+
@sequence = 0
|
60
|
+
@clock_id = rand(2**16)
|
61
|
+
else
|
62
|
+
@sequence = 0
|
63
|
+
end
|
64
|
+
@last_usecs = usecs + @sequence
|
65
|
+
from_usecs(@last_usecs)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a new UUID with a time component based on the specified Time.
|
69
|
+
# A piece of jitter is added to ensure that multiple calls with the same
|
70
|
+
# time do not generate the same UUID (if you want determinism you can set
|
71
|
+
# the second parameter to zero).
|
72
|
+
#
|
73
|
+
# @param [Time] time the time to encode into the UUID
|
74
|
+
# @param [Integer] jitter a number of microseconds to add to the time to make it unique
|
75
|
+
# @return [Cql::TimeUuid] a new UUID
|
76
|
+
#
|
77
|
+
def from_time(time, jitter=rand(2**16))
|
78
|
+
usecs = time.to_i * 1_000_000 + time.usec + jitter
|
79
|
+
from_usecs(usecs)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @private
|
83
|
+
def from_usecs(usecs)
|
84
|
+
t = GREGORIAN_OFFSET + usecs * 10
|
85
|
+
time_hi = t & 0x0fff000000000000
|
86
|
+
time_mid = t & 0x0000ffff00000000
|
87
|
+
time_low = t & 0x00000000ffffffff
|
88
|
+
version = 1
|
89
|
+
clock_id = @clock_id & 0x3fff
|
90
|
+
node_id = @node_id & 0xffffffffffff
|
91
|
+
variant = 0x8000
|
92
|
+
n = (time_low << 96) | (time_mid << 48) | (time_hi << 16)
|
93
|
+
n |= version << 76
|
94
|
+
n |= (clock_id | variant) << 48
|
95
|
+
n |= node_id
|
96
|
+
TimeUuid.new(n)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
GREGORIAN_OFFSET = 122192928000000000
|
103
|
+
end
|
104
|
+
end
|
data/lib/cql/uuid.rb
CHANGED
@@ -6,6 +6,8 @@ module Cql
|
|
6
6
|
# This is a very basic implementation of UUIDs and exists more or less just
|
7
7
|
# to encode and decode UUIDs from and to Cassandra.
|
8
8
|
#
|
9
|
+
# If you want to generate UUIDs see {Cql::TimeUuid::Generator}.
|
10
|
+
#
|
9
11
|
class Uuid
|
10
12
|
# Creates a new UUID either from a string (expected to be on the standard
|
11
13
|
# 8-4-4-4-12 form, or just 32 characters without hyphens), or from a
|
@@ -50,7 +52,7 @@ module Cql
|
|
50
52
|
|
51
53
|
# @private
|
52
54
|
def eql?(other)
|
53
|
-
|
55
|
+
self.value == other.value
|
54
56
|
end
|
55
57
|
alias_method :==, :eql?
|
56
58
|
|
@@ -69,4 +71,4 @@ module Cql
|
|
69
71
|
n
|
70
72
|
end
|
71
73
|
end
|
72
|
-
end
|
74
|
+
end
|
data/lib/cql/version.rb
CHANGED
@@ -39,10 +39,15 @@ module Cql
|
|
39
39
|
client.connect.get.should equal(client)
|
40
40
|
end
|
41
41
|
|
42
|
-
it '
|
42
|
+
it 'connects to the right host and port' do
|
43
43
|
client.connect.get
|
44
|
-
|
45
|
-
|
44
|
+
last_connection.host.should == 'example.com'
|
45
|
+
last_connection.port.should == 12321
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'connects with the default connection timeout' do
|
49
|
+
client.connect.get
|
50
|
+
last_connection.timeout.should == 10
|
46
51
|
end
|
47
52
|
|
48
53
|
it 'sends a startup request' do
|
@@ -58,7 +63,7 @@ module Cql
|
|
58
63
|
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
59
64
|
c.connect.get
|
60
65
|
connections.each do |cc|
|
61
|
-
cc
|
66
|
+
cc.requests.last.should be_a(Protocol::StartupRequest)
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
@@ -80,12 +85,12 @@ module Cql
|
|
80
85
|
end
|
81
86
|
|
82
87
|
it 're-raises any errors raised' do
|
83
|
-
io_reactor.stub(:
|
88
|
+
io_reactor.stub(:connect).and_raise(ArgumentError)
|
84
89
|
expect { client.connect.get }.to raise_error(ArgumentError)
|
85
90
|
end
|
86
91
|
|
87
92
|
it 'is not connected if an error is raised' do
|
88
|
-
io_reactor.stub(:
|
93
|
+
io_reactor.stub(:connect).and_raise(ArgumentError)
|
89
94
|
client.connect.get rescue nil
|
90
95
|
client.should_not be_connected
|
91
96
|
io_reactor.should_not be_running
|
@@ -97,18 +102,18 @@ module Cql
|
|
97
102
|
end
|
98
103
|
|
99
104
|
it 'is not connected while connecting' do
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
f.get
|
106
|
-
end
|
105
|
+
io_reactor.stop.get
|
106
|
+
f = client.connect
|
107
|
+
client.should_not be_connected
|
108
|
+
io_reactor.start.get
|
109
|
+
f.get
|
107
110
|
end
|
108
111
|
|
109
112
|
context 'when the server requests authentication' do
|
110
113
|
before do
|
111
|
-
io_reactor.
|
114
|
+
io_reactor.on_connection do |connection|
|
115
|
+
connection.queue_response(Protocol::AuthenticateResponse.new('com.example.Auth'))
|
116
|
+
end
|
112
117
|
end
|
113
118
|
|
114
119
|
it 'sends credentials' do
|
@@ -123,13 +128,17 @@ module Cql
|
|
123
128
|
end
|
124
129
|
|
125
130
|
it 'raises an error when the server responds with an error to the credentials request' do
|
126
|
-
io_reactor.
|
131
|
+
io_reactor.on_connection do |connection|
|
132
|
+
connection.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
133
|
+
end
|
127
134
|
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
128
135
|
expect { client.connect.get }.to raise_error(AuthenticationError)
|
129
136
|
end
|
130
137
|
|
131
138
|
it 'shuts down the client when there is an authentication error' do
|
132
|
-
io_reactor.
|
139
|
+
io_reactor.on_connection do |connection|
|
140
|
+
connection.queue_response(Protocol::ErrorResponse.new(256, 'No way, José'))
|
141
|
+
end
|
133
142
|
client = described_class.new(connection_options.merge(credentials: {'username' => 'foo', 'password' => 'bar'}))
|
134
143
|
client.connect.get rescue nil
|
135
144
|
client.should_not be_connected
|
@@ -171,7 +180,9 @@ module Cql
|
|
171
180
|
end
|
172
181
|
|
173
182
|
it 'executes a USE query' do
|
174
|
-
io_reactor.
|
183
|
+
io_reactor.on_connection do |connection|
|
184
|
+
connection.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
185
|
+
end
|
175
186
|
client.use('system').get
|
176
187
|
last_request.should == Protocol::QueryRequest.new('USE system', :one)
|
177
188
|
end
|
@@ -185,7 +196,7 @@ module Cql
|
|
185
196
|
c.connect.get
|
186
197
|
|
187
198
|
c.use('system').get
|
188
|
-
last_requests = connections.select { |c| c
|
199
|
+
last_requests = connections.select { |c| c.host =~ /^h\d\.example\.com$/ }.sort_by(&:host).map { |c| c.requests.last }
|
189
200
|
last_requests.should == [
|
190
201
|
Protocol::QueryRequest.new('USE system', :one),
|
191
202
|
Protocol::QueryRequest.new('USE system', :one),
|
@@ -194,7 +205,7 @@ module Cql
|
|
194
205
|
end
|
195
206
|
|
196
207
|
it 'knows which keyspace it changed to' do
|
197
|
-
|
208
|
+
last_connection.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
198
209
|
client.use('system').get
|
199
210
|
client.keyspace.should == 'system'
|
200
211
|
end
|
@@ -202,13 +213,6 @@ module Cql
|
|
202
213
|
it 'raises an error if the keyspace name is not valid' do
|
203
214
|
expect { client.use('system; DROP KEYSPACE system').get }.to raise_error(InvalidKeyspaceNameError)
|
204
215
|
end
|
205
|
-
|
206
|
-
it 'allows the keyspace name to be quoted' do
|
207
|
-
io_reactor.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
208
|
-
client.connect.get
|
209
|
-
client.use('"system"').get
|
210
|
-
client.keyspace.should == "system"
|
211
|
-
end
|
212
216
|
end
|
213
217
|
|
214
218
|
describe '#execute' do
|
@@ -228,7 +232,7 @@ module Cql
|
|
228
232
|
|
229
233
|
context 'with a void CQL query' do
|
230
234
|
it 'returns nil' do
|
231
|
-
|
235
|
+
last_connection.queue_response(Protocol::VoidResultResponse.new)
|
232
236
|
result = client.execute('UPDATE stuff SET thing = 1 WHERE id = 3').get
|
233
237
|
result.should be_nil
|
234
238
|
end
|
@@ -236,13 +240,13 @@ module Cql
|
|
236
240
|
|
237
241
|
context 'with a USE query' do
|
238
242
|
it 'returns nil' do
|
239
|
-
|
243
|
+
last_connection.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
240
244
|
result = client.execute('USE system').get
|
241
245
|
result.should be_nil
|
242
246
|
end
|
243
247
|
|
244
248
|
it 'knows which keyspace it changed to' do
|
245
|
-
|
249
|
+
last_connection.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
246
250
|
client.execute('USE system').get
|
247
251
|
client.keyspace.should == 'system'
|
248
252
|
end
|
@@ -255,14 +259,14 @@ module Cql
|
|
255
259
|
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
256
260
|
c.connect.get
|
257
261
|
|
258
|
-
|
259
|
-
|
260
|
-
|
262
|
+
connections.find { |c| c.host == 'h1.example.com' }.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
263
|
+
connections.find { |c| c.host == 'h2.example.com' }.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
264
|
+
connections.find { |c| c.host == 'h3.example.com' }.queue_response(Protocol::SetKeyspaceResultResponse.new('system'))
|
261
265
|
|
262
266
|
c.execute('USE system', :one).get
|
263
267
|
c.keyspace.should == 'system'
|
264
268
|
|
265
|
-
last_requests = connections.select { |c| c
|
269
|
+
last_requests = connections.select { |c| c.host =~ /^h\d\.example\.com$/ }.sort_by(&:host).map { |c| c.requests.last }
|
266
270
|
last_requests.should == [
|
267
271
|
Protocol::QueryRequest.new('USE system', :one),
|
268
272
|
Protocol::QueryRequest.new('USE system', :one),
|
@@ -281,7 +285,7 @@ module Cql
|
|
281
285
|
end
|
282
286
|
|
283
287
|
let :result do
|
284
|
-
|
288
|
+
last_connection.queue_response(Protocol::RowsResultResponse.new(rows, metadata))
|
285
289
|
client.execute('SELECT * FROM things').get
|
286
290
|
end
|
287
291
|
|
@@ -324,12 +328,12 @@ module Cql
|
|
324
328
|
|
325
329
|
context 'when the response is an error' do
|
326
330
|
it 'raises an error' do
|
327
|
-
|
331
|
+
last_connection.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
328
332
|
expect { client.execute('SELECT * FROM things').get }.to raise_error(QueryError, 'Blurgh')
|
329
333
|
end
|
330
334
|
|
331
335
|
it 'decorates the error with the CQL that caused it' do
|
332
|
-
|
336
|
+
last_connection.queue_response(Protocol::ErrorResponse.new(0xabcd, 'Blurgh'))
|
333
337
|
begin
|
334
338
|
client.execute('SELECT * FROM things').get
|
335
339
|
rescue QueryError => e
|
@@ -360,59 +364,31 @@ module Cql
|
|
360
364
|
end
|
361
365
|
|
362
366
|
it 'returns a prepared statement' do
|
363
|
-
|
367
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new('A' * 32, [['stuff', 'things', 'item', :varchar]]))
|
364
368
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
365
369
|
statement.should_not be_nil
|
366
370
|
end
|
367
371
|
|
368
372
|
it 'executes a prepared statement' do
|
369
|
-
|
373
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
370
374
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
371
375
|
statement.execute('foo').get
|
372
376
|
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['foo'], :quorum)
|
373
377
|
end
|
374
378
|
|
375
379
|
it 'returns a prepared statement that knows the metadata' do
|
376
|
-
|
380
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
377
381
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
378
382
|
statement.metadata['item'].type == :varchar
|
379
383
|
end
|
380
384
|
|
381
385
|
it 'executes a prepared statement with a specific consistency level' do
|
382
|
-
|
386
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
383
387
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
384
388
|
statement.execute('thing', :local_quorum).get
|
385
389
|
last_request.should == Protocol::ExecuteRequest.new(id, metadata, ['thing'], :local_quorum)
|
386
390
|
end
|
387
391
|
|
388
|
-
it 'executes a prepared statement using the right connection' do
|
389
|
-
client.close.get
|
390
|
-
io_reactor.stop.get
|
391
|
-
io_reactor.start.get
|
392
|
-
|
393
|
-
c = described_class.new(connection_options.merge(host: 'h1.example.com,h2.example.com,h3.example.com'))
|
394
|
-
c.connect.get
|
395
|
-
|
396
|
-
io_reactor.queue_response(Protocol::PreparedResultResponse.new('A' * 32, metadata))
|
397
|
-
io_reactor.queue_response(Protocol::PreparedResultResponse.new('B' * 32, metadata))
|
398
|
-
io_reactor.queue_response(Protocol::PreparedResultResponse.new('C' * 32, metadata))
|
399
|
-
|
400
|
-
statement1 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
401
|
-
statement1_connection = io_reactor.last_used_connection
|
402
|
-
statement2 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
403
|
-
statement2_connection = io_reactor.last_used_connection
|
404
|
-
statement3 = c.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
405
|
-
statement3_connection = io_reactor.last_used_connection
|
406
|
-
|
407
|
-
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo1'}], metadata), statement1_connection[:host])
|
408
|
-
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo2'}], metadata), statement2_connection[:host])
|
409
|
-
io_reactor.queue_response(Protocol::RowsResultResponse.new([{'thing' => 'foo3'}], metadata), statement3_connection[:host])
|
410
|
-
|
411
|
-
statement1.execute('foo').get.first.should == {'thing' => 'foo1'}
|
412
|
-
statement2.execute('foo').get.first.should == {'thing' => 'foo2'}
|
413
|
-
statement3.execute('foo').get.first.should == {'thing' => 'foo3'}
|
414
|
-
end
|
415
|
-
|
416
392
|
context 'when there is an error creating the request' do
|
417
393
|
it 'returns a failed future' do
|
418
394
|
f = client.prepare(nil)
|
@@ -422,7 +398,7 @@ module Cql
|
|
422
398
|
|
423
399
|
context 'when there is an error preparing the request' do
|
424
400
|
it 'returns a failed future' do
|
425
|
-
|
401
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new(id, metadata))
|
426
402
|
statement = client.prepare('SELECT * FROM stuff.things WHERE item = ?').get
|
427
403
|
f = statement.execute
|
428
404
|
expect { f.get }.to raise_error(ArgumentError)
|
@@ -473,7 +449,7 @@ module Cql
|
|
473
449
|
|
474
450
|
it 'complains when #execute of a prepared statement is called after #close' do
|
475
451
|
client.connect.get
|
476
|
-
|
452
|
+
last_connection.queue_response(Protocol::PreparedResultResponse.new('A' * 32, []))
|
477
453
|
statement = client.prepare('DELETE FROM stuff WHERE id = 3').get
|
478
454
|
client.close.get
|
479
455
|
expect { statement.execute.get }.to raise_error(NotConnectedError)
|
@@ -481,4 +457,4 @@ module Cql
|
|
481
457
|
end
|
482
458
|
end
|
483
459
|
end
|
484
|
-
end
|
460
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
module Client
|
8
|
+
describe AsynchronousPreparedStatement do
|
9
|
+
let :statement do
|
10
|
+
described_class.new(connection, statement_id, raw_metadata)
|
11
|
+
end
|
12
|
+
|
13
|
+
let :connection do
|
14
|
+
stub(:connection)
|
15
|
+
end
|
16
|
+
|
17
|
+
let :statement_id do
|
18
|
+
"\x2a"
|
19
|
+
end
|
20
|
+
|
21
|
+
let :raw_metadata do
|
22
|
+
[
|
23
|
+
['my_keyspace', 'my_table', 'my_column', :int],
|
24
|
+
['my_keyspace', 'my_table', 'my_other_column', :text],
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
let :rows do
|
29
|
+
[
|
30
|
+
{'my_column' => 11, 'my_other_column' => 'hello'},
|
31
|
+
{'my_column' => 22, 'my_other_column' => 'foo'},
|
32
|
+
{'my_column' => 33, 'my_other_column' => 'bar'},
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#metadata' do
|
37
|
+
it 'returns the interpreted metadata' do
|
38
|
+
statement.metadata.should be_a(ResultMetadata)
|
39
|
+
statement.metadata['my_column'].should be_a(ColumnMetadata)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#execute' do
|
44
|
+
it 'creates and sends an EXECUTE request' do
|
45
|
+
expected_request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :one)
|
46
|
+
connection.should_receive(:send_request).with(expected_request)
|
47
|
+
statement.execute(11, 'hello', :one)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns a future that resolves to a QueryResult' do
|
51
|
+
request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :two)
|
52
|
+
response = Cql::Protocol::RowsResultResponse.new(rows, raw_metadata)
|
53
|
+
connection.stub(:send_request).with(request).and_return(Future.completed(response))
|
54
|
+
result = statement.execute(11, 'hello', :two).get
|
55
|
+
result.metadata['my_other_column'].should == ColumnMetadata.new('my_keyspace', 'my_table', 'my_other_column', :text)
|
56
|
+
result.first.should == {'my_column' => 11, 'my_other_column' => 'hello'}
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'returns a failed future when the number of arguments is wrong' do
|
60
|
+
f1 = statement.execute(11, :one)
|
61
|
+
f2 = statement.execute(11, 'foo', 22, :one)
|
62
|
+
expect { f1.get }.to raise_error
|
63
|
+
expect { f2.get }.to raise_error
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|