cql-rb 1.0.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 +13 -0
- data/bin/cqlexec +135 -0
- data/lib/cql.rb +11 -0
- data/lib/cql/client.rb +196 -0
- data/lib/cql/future.rb +176 -0
- data/lib/cql/io.rb +13 -0
- data/lib/cql/io/io_reactor.rb +351 -0
- data/lib/cql/protocol.rb +39 -0
- data/lib/cql/protocol/decoding.rb +156 -0
- data/lib/cql/protocol/encoding.rb +109 -0
- data/lib/cql/protocol/request_frame.rb +228 -0
- data/lib/cql/protocol/response_frame.rb +551 -0
- data/lib/cql/uuid.rb +46 -0
- data/lib/cql/version.rb +5 -0
- data/spec/cql/client_spec.rb +368 -0
- data/spec/cql/future_spec.rb +297 -0
- data/spec/cql/io/io_reactor_spec.rb +290 -0
- data/spec/cql/protocol/decoding_spec.rb +464 -0
- data/spec/cql/protocol/encoding_spec.rb +338 -0
- data/spec/cql/protocol/request_frame_spec.rb +359 -0
- data/spec/cql/protocol/response_frame_spec.rb +746 -0
- data/spec/cql/uuid_spec.rb +40 -0
- data/spec/integration/client_spec.rb +101 -0
- data/spec/integration/protocol_spec.rb +326 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/fake_io_reactor.rb +55 -0
- data/spec/support/fake_server.rb +95 -0
- metadata +87 -0
@@ -0,0 +1,290 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
module Io
|
8
|
+
describe IoReactor do
|
9
|
+
let :host do
|
10
|
+
Socket.gethostname
|
11
|
+
end
|
12
|
+
|
13
|
+
let :port do
|
14
|
+
34535
|
15
|
+
end
|
16
|
+
|
17
|
+
let :server do
|
18
|
+
FakeServer.new(port)
|
19
|
+
end
|
20
|
+
|
21
|
+
let :io_reactor do
|
22
|
+
described_class.new(connection_timeout: 1)
|
23
|
+
end
|
24
|
+
|
25
|
+
before do
|
26
|
+
server.start!
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
io_reactor.stop.get if io_reactor.running?
|
31
|
+
server.stop!
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#initialize' do
|
35
|
+
it 'does not connect' do
|
36
|
+
described_class.new
|
37
|
+
sleep(0.1)
|
38
|
+
server.connects.should == 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#running?' do
|
43
|
+
it 'is initially false' do
|
44
|
+
io_reactor.should_not be_running
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'is true when started' do
|
48
|
+
io_reactor.start.get
|
49
|
+
io_reactor.should be_running
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'is true when starting' do
|
53
|
+
f = io_reactor.start
|
54
|
+
io_reactor.should be_running
|
55
|
+
f.get
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'is false when stopped' do
|
59
|
+
io_reactor.start.get
|
60
|
+
io_reactor.stop.get
|
61
|
+
io_reactor.should_not be_running
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#stop' do
|
66
|
+
it 'closes all connections' do
|
67
|
+
io_reactor.start.get
|
68
|
+
f1 = io_reactor.add_connection(host, port)
|
69
|
+
f2 = io_reactor.add_connection(host, port)
|
70
|
+
f3 = io_reactor.add_connection(host, port)
|
71
|
+
f4 = io_reactor.add_connection(host, port)
|
72
|
+
Future.combine(f1, f2, f3, f4).get
|
73
|
+
io_reactor.stop.get
|
74
|
+
server.await_disconnects!(4)
|
75
|
+
server.disconnects.should == 4
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'succeeds connection futures when stopping while connecting' do
|
79
|
+
f = io_reactor.add_connection(host, port + 9)
|
80
|
+
io_reactor.start
|
81
|
+
io_reactor.stop
|
82
|
+
f.get
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#add_connection' do
|
87
|
+
before do
|
88
|
+
io_reactor.start.get
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'connects to the specified host and port' do
|
92
|
+
future = io_reactor.add_connection(host, port)
|
93
|
+
future.get
|
94
|
+
server.await_connects!(1)
|
95
|
+
server.connects.should == 1
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'yields the connection ID when completed' do
|
99
|
+
future = io_reactor.add_connection(host, port)
|
100
|
+
future.get.should_not be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'fails the returned future when it cannot connect to the host' do
|
104
|
+
future = io_reactor.add_connection('example.com', port)
|
105
|
+
expect { future.get }.to raise_error(ConnectionError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'fails the returned future when it cannot connect to the port' do
|
109
|
+
future = io_reactor.add_connection(host, 9999)
|
110
|
+
expect { future.get }.to raise_error(ConnectionError)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'times out quickly when it cannot connect' do
|
114
|
+
started_at = Time.now
|
115
|
+
begin
|
116
|
+
future = io_reactor.add_connection(host, 9999)
|
117
|
+
future.get
|
118
|
+
rescue ConnectionError
|
119
|
+
end
|
120
|
+
time_taken = (Time.now - started_at).to_f
|
121
|
+
time_taken.should be < 1.5
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'can be called before the reactor is started' do
|
125
|
+
r = described_class.new(connection_timeout: 1)
|
126
|
+
f1 = r.add_connection(host, port)
|
127
|
+
f2 = r.start
|
128
|
+
f2.get
|
129
|
+
f1.get
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#queue_request' do
|
134
|
+
it 'eventually sends the request' do
|
135
|
+
io_reactor.start
|
136
|
+
io_reactor.add_connection(host, port).get
|
137
|
+
io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
|
138
|
+
sleep(0.01) until server.received_bytes.size > 0
|
139
|
+
server.received_bytes[3, 1].should == "\x01"
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'can be called before the reactor is started' do
|
143
|
+
io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
|
144
|
+
io_reactor.start
|
145
|
+
io_reactor.add_connection(host, port).get
|
146
|
+
sleep(0.01) until server.received_bytes.size > 0
|
147
|
+
server.received_bytes[3, 1].should == "\x01"
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'queues requests when all connections are busy' do
|
151
|
+
request = Cql::Protocol::QueryRequest.new('UPDATE x SET y = 1 WHERE z = 2', :one)
|
152
|
+
|
153
|
+
io_reactor.start
|
154
|
+
io_reactor.add_connection(host, port).get
|
155
|
+
|
156
|
+
futures = 200.times.map do
|
157
|
+
io_reactor.queue_request(request)
|
158
|
+
end
|
159
|
+
|
160
|
+
128.times do |i|
|
161
|
+
server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
|
162
|
+
end
|
163
|
+
|
164
|
+
Future.combine(*futures.shift(128)).get
|
165
|
+
|
166
|
+
128.times do |i|
|
167
|
+
server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
|
168
|
+
end
|
169
|
+
|
170
|
+
Future.combine(*futures).get
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'performs the request using the connection with the given ID' do
|
174
|
+
future = Future.new
|
175
|
+
request = Cql::Protocol::StartupRequest.new
|
176
|
+
response = "\x81\x00\x00\x02\x00\x00\x00\x00"
|
177
|
+
|
178
|
+
io_reactor.start.on_complete do
|
179
|
+
io_reactor.add_connection(host, port).on_complete do |c1_id|
|
180
|
+
io_reactor.add_connection(host, port).on_complete do |c2_id|
|
181
|
+
q1_future = io_reactor.queue_request(request, c2_id)
|
182
|
+
q2_future = io_reactor.queue_request(request, c1_id)
|
183
|
+
|
184
|
+
Future.combine(q1_future, q2_future).on_complete do |(_, q1_id), (_, q2_id)|
|
185
|
+
future.complete!([c1_id, c2_id, q1_id, q2_id])
|
186
|
+
end
|
187
|
+
|
188
|
+
server.await_connects!(2)
|
189
|
+
server.broadcast!(response.dup)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
connection1_id, connection2_id, query1_id, query2_id = future.value
|
195
|
+
|
196
|
+
connection1_id.should_not be_nil
|
197
|
+
connection2_id.should_not be_nil
|
198
|
+
query1_id.should == connection2_id
|
199
|
+
query2_id.should == connection1_id
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'fails if the connection does not exist' do
|
203
|
+
f = io_reactor.start.flat_map do
|
204
|
+
io_reactor.add_connection(host, port).flat_map do
|
205
|
+
io_reactor.queue_request(Cql::Protocol::StartupRequest.new, 1234)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
expect { f.get }.to raise_error(ConnectionNotFoundError)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'fails if the connection is busy' do
|
212
|
+
f = io_reactor.start.flat_map do
|
213
|
+
io_reactor.add_connection(host, port).flat_map do
|
214
|
+
io_reactor.add_connection(host, port).flat_map do |connection_id|
|
215
|
+
200.times do
|
216
|
+
io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
|
217
|
+
end
|
218
|
+
io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
expect { f.get }.to raise_error(ConnectionBusyError)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'fails if the connection is busy, when there is only one connection' do
|
226
|
+
pending 'as it is the reactor doesn\'t try to deliver requests when all connections are busy'
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'yields the response when completed' do
|
230
|
+
response = nil
|
231
|
+
io_reactor.start
|
232
|
+
io_reactor.add_connection(host, port).get
|
233
|
+
f = io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
|
234
|
+
f.on_complete do |r, _|
|
235
|
+
response = r
|
236
|
+
end
|
237
|
+
server.broadcast!("\x81\x00\x00\x02\x00\x00\x00\x00")
|
238
|
+
sleep(0.01) until response
|
239
|
+
response.should == Cql::Protocol::ReadyResponse.new
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'yields the connection ID when completed' do
|
243
|
+
connection = nil
|
244
|
+
io_reactor.start
|
245
|
+
io_reactor.add_connection(host, port).get
|
246
|
+
f = io_reactor.queue_request(Cql::Protocol::StartupRequest.new)
|
247
|
+
f.on_complete do |_, c|
|
248
|
+
connection = c
|
249
|
+
end
|
250
|
+
server.broadcast!("\x81\x00\x00\x02\x00\x00\x00\x00")
|
251
|
+
sleep(0.01) until connection
|
252
|
+
connection.should_not be_nil
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe '#add_event_listener' do
|
257
|
+
it 'calls the listener when frames with stream ID -1 arrives' do
|
258
|
+
event = nil
|
259
|
+
io_reactor.start
|
260
|
+
io_reactor.add_connection(host, port).get
|
261
|
+
io_reactor.add_event_listener { |e| event = e }
|
262
|
+
server.broadcast!("\x81\x00\xFF\f\x00\x00\x00+\x00\rSCHEMA_CHANGE\x00\aDROPPED\x00\nkeyspace01\x00\x05users")
|
263
|
+
sleep(0.01) until event
|
264
|
+
event.should == Cql::Protocol::SchemaChangeEventResponse.new('DROPPED', 'keyspace01', 'users')
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
context 'when errors occur' do
|
269
|
+
context 'in the IO loop' do
|
270
|
+
before do
|
271
|
+
bad_request = stub(:request)
|
272
|
+
bad_request.stub(:opcode).and_raise(StandardError.new('Blurgh'))
|
273
|
+
io_reactor.start
|
274
|
+
io_reactor.add_connection(host, port).get
|
275
|
+
io_reactor.queue_request(bad_request)
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'stops' do
|
279
|
+
sleep(0.1)
|
280
|
+
io_reactor.should_not be_running
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'fails the future returned from #stop' do
|
284
|
+
expect { io_reactor.stop.get }.to raise_error('Blurgh')
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,464 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
module Protocol
|
8
|
+
describe Decoding do
|
9
|
+
describe '#read_byte!' do
|
10
|
+
let :buffer do
|
11
|
+
"\xab"
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'decodes a raw byte' do
|
15
|
+
Decoding.read_byte!(buffer).should == 0xab
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'consumes the byte' do
|
19
|
+
Decoding.read_byte!(buffer)
|
20
|
+
buffer.should be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises an error when there is no byte available' do
|
24
|
+
expect { Decoding.read_byte!('') }.to raise_error(DecodingError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#read_varint!' do
|
29
|
+
it 'decodes a variable length integer' do
|
30
|
+
Decoding.read_varint!("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[", 17).should == 1231312312331283012830129382342342412123
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'decodes a negative variable length integer' do
|
34
|
+
Decoding.read_varint!("\xC9v\x8D:\x86", 5).should == -234234234234
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'decodes an unsigned variable length integer' do
|
38
|
+
Decoding.read_varint!("\xC9v\x8D:\x86", 5, false).should == 865277393542
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'consumes the bytes' do
|
42
|
+
buffer = "\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[\x01\x02\x03"
|
43
|
+
Decoding.read_varint!(buffer, 17)
|
44
|
+
buffer.should == "\x01\x02\x03"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises an error when there is not enough bytes available' do
|
48
|
+
expect { Decoding.read_varint!("\xC9v\x8D:", 7) }.to raise_error(DecodingError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#read_decimal!' do
|
53
|
+
let :buffer do
|
54
|
+
"\x00\x00\x00\x12\r'\xFDI\xAD\x80f\x11g\xDCfV\xAA"
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'decodes a decimal to a BigDecimal' do
|
58
|
+
Decoding.read_decimal!(buffer, buffer.length).should == BigDecimal.new('1042342234234.123423435647768234')
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'consumes the bytes' do
|
62
|
+
buffer << 'HELLO'
|
63
|
+
Decoding.read_decimal!(buffer, buffer.length - 5)
|
64
|
+
buffer.should == 'HELLO'
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'defaults to using the buffer length' do
|
68
|
+
Decoding.read_decimal!(buffer.dup).should == Decoding.read_decimal!(buffer, buffer.length)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'raises an error when there is not enough bytes available' do
|
72
|
+
expect { Decoding.read_decimal!(buffer[0, 3], 7) }.to raise_error(DecodingError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#read_long!' do
|
77
|
+
it 'decodes a long' do
|
78
|
+
Decoding.read_long!("\x00\x00\xca\xfe\xba\xbe\x00\x00").should == 0x0000cafebabe0000
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'consumes the bytes' do
|
82
|
+
buffer = "\xca\xfe\xba\xbe\xca\xfe\xba\xbe\xca\xfe\xba\xbe"
|
83
|
+
Decoding.read_long!(buffer)
|
84
|
+
buffer.should == "\xca\xfe\xba\xbe"
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'raises an error when there is not enough bytes available' do
|
88
|
+
expect { Decoding.read_long!("\xca\xfe\xba\xbe\x00") }.to raise_error(DecodingError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#read_double!' do
|
93
|
+
it 'decodes a double' do
|
94
|
+
Decoding.read_double!("@\xC3\x88\x0F\xC2\x7F\x9DU").should == 10000.123123123
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'consumes the bytes' do
|
98
|
+
buffer = "@\xC3\x88\x0F\xC2\x7F\x9DUxyz"
|
99
|
+
Decoding.read_double!(buffer)
|
100
|
+
buffer.should == 'xyz'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'raises an error when there is not enough bytes available' do
|
104
|
+
expect { Decoding.read_double!("@\xC3\x88\x0F") }.to raise_error(DecodingError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '#read_float!' do
|
109
|
+
it 'decodes a float' do
|
110
|
+
Decoding.read_float!("AB\x14{").should be_within(0.00001).of(12.13)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'consumes the bytes' do
|
114
|
+
buffer = "AB\x14{xyz"
|
115
|
+
Decoding.read_float!(buffer)
|
116
|
+
buffer.should == 'xyz'
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raises an error when there is not enough bytes available' do
|
120
|
+
expect { Decoding.read_float!("\x0F") }.to raise_error(DecodingError)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '#read_int!' do
|
125
|
+
let :buffer do
|
126
|
+
"\x00\xff\x00\xff"
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'decodes an int' do
|
130
|
+
Decoding.read_int!(buffer).should == 0x00ff00ff
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'consumes the bytes' do
|
134
|
+
buffer << "\xab\xcd"
|
135
|
+
Decoding.read_int!(buffer)
|
136
|
+
buffer.should == "\xab\xcd"
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
140
|
+
expect { Decoding.read_int!("\x01\xab") }.to raise_error(DecodingError)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#read_short!' do
|
145
|
+
let :buffer do
|
146
|
+
"\x00\x02"
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'decodes a short' do
|
150
|
+
Decoding.read_short!(buffer).should == 2
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'consumes the bytes' do
|
154
|
+
buffer << "\xff\xff"
|
155
|
+
Decoding.read_short!(buffer)
|
156
|
+
buffer.should == "\xff\xff"
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
160
|
+
expect { Decoding.read_short!("\x01") }.to raise_error(DecodingError)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#read_string!' do
|
165
|
+
let :buffer do
|
166
|
+
"\x00\x0bhej och hå"
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'decodes a string' do
|
170
|
+
Decoding.read_string!(buffer).should == 'hej och hå'.force_encoding(::Encoding::UTF_8)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'decodes a string as UTF-8' do
|
174
|
+
Decoding.read_string!(buffer).encoding.should == ::Encoding::UTF_8
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'decodes an empty string' do
|
178
|
+
Decoding.read_string!("\x00\x00").should be_empty
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'consumes the bytes' do
|
182
|
+
buffer << "\xff\xff"
|
183
|
+
Decoding.read_string!(buffer)
|
184
|
+
buffer.should == "\xff\xff"
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
188
|
+
expect { Decoding.read_string!(buffer.slice(0, 5)) }.to raise_error(DecodingError)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#read_long_string!' do
|
193
|
+
let :buffer do
|
194
|
+
"\x00\x01\x00\00" << ('x' * 0x10000)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'decodes a string' do
|
198
|
+
Decoding.read_long_string!(buffer.dup).should start_with('xxx')
|
199
|
+
Decoding.read_long_string!(buffer).length.should == 0x10000
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'decodes a string as UTF-8' do
|
203
|
+
Decoding.read_long_string!(buffer).encoding.should == ::Encoding::UTF_8
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'consumes the bytes' do
|
207
|
+
buffer << "\xff\xff"
|
208
|
+
Decoding.read_long_string!(buffer)
|
209
|
+
buffer.should == "\xff\xff"
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
213
|
+
expect { Decoding.read_long_string!(buffer.slice(0, 246)) }.to raise_error(DecodingError)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe '#read_uuid!' do
|
218
|
+
let :buffer do
|
219
|
+
"\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11"
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'decodes a UUID' do
|
223
|
+
Decoding.read_uuid!(buffer).should == Uuid.new('a4a70900-24e1-11df-8924-001ff3591711')
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'consumes the bytes' do
|
227
|
+
Decoding.read_uuid!(buffer)
|
228
|
+
buffer.should be_empty
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'raises an error when there a not enough bytes in the buffer' do
|
232
|
+
expect { Decoding.read_uuid!(buffer[2, 5]) }.to raise_error(DecodingError)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#read_string_list!' do
|
237
|
+
let :buffer do
|
238
|
+
"\x00\x02\x00\x05hello\x00\x05world"
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'decodes a string list' do
|
242
|
+
Decoding.read_string_list!(buffer).should == %w[hello world]
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'decodes an empty string list' do
|
246
|
+
Decoding.read_string_list!("\x00\x00").should == []
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'consumes the bytes' do
|
250
|
+
buffer << "\xff\xff"
|
251
|
+
Decoding.read_string_list!(buffer)
|
252
|
+
buffer.should == "\xff\xff"
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
256
|
+
expect { Decoding.read_string_list!(buffer.slice(0, 13)) }.to raise_error(DecodingError)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe '#read_bytes!' do
|
261
|
+
let :buffer do
|
262
|
+
"\x00\x01\x00\x00" << ("\x42" * 0x10000)
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'decodes a byte array' do
|
266
|
+
Decoding.read_bytes!(buffer).should == ("\x42" * 0x10000)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'decodes an empty byte array' do
|
270
|
+
Decoding.read_bytes!("\x00\x00\x00\x00").should == ''
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'returns an ASCII-8BIT encoded string' do
|
274
|
+
Decoding.read_bytes!("\x00\x00\x00\x01\xaa").encoding.should == ::Encoding::BINARY
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'decodes null' do
|
278
|
+
Decoding.read_bytes!("\x80\x00\x00\x00").should be_nil
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'consumes the bytes' do
|
282
|
+
buffer << "\xab\xcd"
|
283
|
+
Decoding.read_bytes!(buffer)
|
284
|
+
buffer.should == "\xab\xcd"
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
288
|
+
expect { Decoding.read_bytes!(buffer[0, 10]) }.to raise_error(DecodingError)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe '#read_short_bytes!' do
|
293
|
+
let :buffer do
|
294
|
+
"\x01\x00" << ("\x42" * 0x100)
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'decodes a byte array' do
|
298
|
+
Decoding.read_short_bytes!(buffer).should == ("\x42" * 0x100)
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'decodes an empty byte array' do
|
302
|
+
Decoding.read_short_bytes!("\x00\x00\x00\x00").should == ''
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'returns an ASCII-8BIT encoded string' do
|
306
|
+
Decoding.read_short_bytes!("\x00\x00\x00\x01\xaa").encoding.should == ::Encoding::BINARY
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'decodes null' do
|
310
|
+
Decoding.read_short_bytes!("\x80\x00").should be_nil
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'consumes the bytes' do
|
314
|
+
buffer << "\xab\xcd"
|
315
|
+
Decoding.read_short_bytes!(buffer)
|
316
|
+
buffer.should == "\xab\xcd"
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
320
|
+
expect { Decoding.read_short_bytes!(buffer[0, 10]) }.to raise_error(DecodingError)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
describe '#read_option!' do
|
325
|
+
it 'decodes an option ID and value with instructions from a block' do
|
326
|
+
id, value = Decoding.read_option!("\x00\x01\x00\x03foo") do |id, buffer|
|
327
|
+
Decoding.read_string!(buffer)
|
328
|
+
end
|
329
|
+
id.should == 1
|
330
|
+
value.should == 'foo'
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'decodes an option ID and nil value when there is no block' do
|
334
|
+
id, value = Decoding.read_option!("\xaa\xbb")
|
335
|
+
id.should == 0xaabb
|
336
|
+
value.should be_nil
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'consumes the bytes' do
|
340
|
+
buffer = "\x00\x01\x00\x03\xab"
|
341
|
+
id, value = Decoding.read_option!(buffer) do |id, buffer|
|
342
|
+
Decoding.read_short!(buffer)
|
343
|
+
end
|
344
|
+
buffer.should == "\xab"
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
348
|
+
expect { Decoding.read_option!("\xaa") }.to raise_error(DecodingError)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe '#read_inet!' do
|
353
|
+
it 'decodes an IPv4 + port pair' do
|
354
|
+
ip_addr, port = Decoding.read_inet!("\x04\x00\x00\x00\x00\x00\x00#R")
|
355
|
+
ip_addr.should == IPAddr.new('0.0.0.0')
|
356
|
+
port.should == 9042
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'decodes an IPv6 + port pair' do
|
360
|
+
ip_addr, port = Decoding.read_inet!("\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00#R")
|
361
|
+
ip_addr.should == IPAddr.new('::1')
|
362
|
+
port.should == 9042
|
363
|
+
end
|
364
|
+
|
365
|
+
it 'consumes the bytes' do
|
366
|
+
buffer = "\x04\x00\x00\x00\x00\x00\x00#R\xff\xaa"
|
367
|
+
Decoding.read_inet!(buffer)
|
368
|
+
buffer.should == "\xff\xaa"
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
372
|
+
expect { Decoding.read_inet!("\x04\x00\x00\x00\x00\x00\x00") }.to raise_error(DecodingError)
|
373
|
+
expect { Decoding.read_inet!("\x04\x00\x00\x00") }.to raise_error(DecodingError)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe '#read_consistency!' do
|
378
|
+
it 'decodes ANY' do
|
379
|
+
Decoding.read_consistency!("\x00\x00").should == :any
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'decodes ONE' do
|
383
|
+
Decoding.read_consistency!("\x00\x01").should == :one
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'decodes TWO' do
|
387
|
+
Decoding.read_consistency!("\x00\x02").should == :two
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'decodes THREE' do
|
391
|
+
Decoding.read_consistency!("\x00\x03").should == :three
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'decodes QUORUM' do
|
395
|
+
Decoding.read_consistency!("\x00\x04").should == :quorum
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'decodes ALL' do
|
399
|
+
Decoding.read_consistency!("\x00\x05").should == :all
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'decodes LOCAL_QUORUM' do
|
403
|
+
Decoding.read_consistency!("\x00\x06").should == :local_quorum
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'decodes EACH_QUORUM' do
|
407
|
+
Decoding.read_consistency!("\x00\x07").should == :each_quorum
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'raises an exception for an unknown consistency' do
|
411
|
+
expect { Decoding.read_consistency!("\xff\xff") }.to raise_error(DecodingError)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe '#read_string_map!' do
|
416
|
+
let :buffer do
|
417
|
+
"\x00\x02\x00\x05hello\x00\x05world\x00\x03foo\x00\x03bar"
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'decodes a string multimap' do
|
421
|
+
Decoding.read_string_map!(buffer).should == {'hello' => 'world', 'foo' => 'bar'}
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'decodes an empty string map' do
|
425
|
+
Decoding.read_string_map!("\x00\x00").should == {}
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'consumes the bytes' do
|
429
|
+
buffer << "\xff"
|
430
|
+
Decoding.read_string_map!(buffer)
|
431
|
+
buffer.should == "\xff"
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
435
|
+
expect { Decoding.read_string_map!(buffer.slice(0, 20)) }.to raise_error(DecodingError)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
describe '#read_string_multimap!' do
|
440
|
+
let :buffer do
|
441
|
+
"\x00\x02\x00\x0bCQL_VERSION\x00\x01\x00\x053.0.0\x00\x0bCOMPRESSION\x00\x02\x00\x06snappy\x00\x04gzip"
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'decodes a string multimap' do
|
445
|
+
Decoding.read_string_multimap!(buffer).should == {'CQL_VERSION' => ['3.0.0'], 'COMPRESSION' => ['snappy', 'gzip']}
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'decodes an empty string multimap' do
|
449
|
+
Decoding.read_string_multimap!("\x00\x00").should == {}
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'consumes the bytes' do
|
453
|
+
buffer << "\xff"
|
454
|
+
Decoding.read_string_multimap!(buffer)
|
455
|
+
buffer.should == "\xff"
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'raises an error when there are not enough bytes in the buffer' do
|
459
|
+
expect { Decoding.read_string_multimap!(buffer.slice(0, 40)) }.to raise_error(DecodingError)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|