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.
@@ -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