cql-rb 2.0.0.pre0 → 2.0.0.pre1

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -2
  3. data/lib/cql.rb +8 -3
  4. data/lib/cql/client.rb +21 -356
  5. data/lib/cql/client/authenticators.rb +70 -0
  6. data/lib/cql/client/batch.rb +54 -0
  7. data/lib/cql/client/{asynchronous_client.rb → client.rb} +241 -6
  8. data/lib/cql/client/connector.rb +3 -2
  9. data/lib/cql/client/{asynchronous_prepared_statement.rb → prepared_statement.rb} +103 -0
  10. data/lib/cql/protocol.rb +1 -2
  11. data/lib/cql/protocol/cql_byte_buffer.rb +285 -0
  12. data/lib/cql/protocol/cql_protocol_handler.rb +3 -3
  13. data/lib/cql/protocol/frame_decoder.rb +3 -3
  14. data/lib/cql/protocol/frame_encoder.rb +2 -2
  15. data/lib/cql/protocol/request.rb +0 -2
  16. data/lib/cql/protocol/requests/auth_response_request.rb +2 -2
  17. data/lib/cql/protocol/requests/batch_request.rb +10 -10
  18. data/lib/cql/protocol/requests/credentials_request.rb +2 -2
  19. data/lib/cql/protocol/requests/execute_request.rb +13 -13
  20. data/lib/cql/protocol/requests/options_request.rb +2 -2
  21. data/lib/cql/protocol/requests/prepare_request.rb +2 -2
  22. data/lib/cql/protocol/requests/query_request.rb +13 -13
  23. data/lib/cql/protocol/requests/register_request.rb +2 -2
  24. data/lib/cql/protocol/requests/startup_request.rb +2 -2
  25. data/lib/cql/protocol/response.rb +2 -4
  26. data/lib/cql/protocol/responses/auth_challenge_response.rb +2 -2
  27. data/lib/cql/protocol/responses/auth_success_response.rb +2 -2
  28. data/lib/cql/protocol/responses/authenticate_response.rb +2 -2
  29. data/lib/cql/protocol/responses/detailed_error_response.rb +15 -15
  30. data/lib/cql/protocol/responses/error_response.rb +4 -4
  31. data/lib/cql/protocol/responses/event_response.rb +3 -3
  32. data/lib/cql/protocol/responses/prepared_result_response.rb +4 -4
  33. data/lib/cql/protocol/responses/raw_rows_result_response.rb +1 -1
  34. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  35. data/lib/cql/protocol/responses/result_response.rb +3 -3
  36. data/lib/cql/protocol/responses/rows_result_response.rb +22 -22
  37. data/lib/cql/protocol/responses/schema_change_event_response.rb +2 -2
  38. data/lib/cql/protocol/responses/schema_change_result_response.rb +2 -2
  39. data/lib/cql/protocol/responses/set_keyspace_result_response.rb +2 -2
  40. data/lib/cql/protocol/responses/status_change_event_response.rb +2 -2
  41. data/lib/cql/protocol/responses/supported_response.rb +2 -2
  42. data/lib/cql/protocol/responses/void_result_response.rb +1 -1
  43. data/lib/cql/protocol/type_converter.rb +78 -81
  44. data/lib/cql/time_uuid.rb +6 -0
  45. data/lib/cql/uuid.rb +2 -1
  46. data/lib/cql/version.rb +1 -1
  47. data/spec/cql/client/batch_spec.rb +8 -8
  48. data/spec/cql/client/{asynchronous_client_spec.rb → client_spec.rb} +162 -0
  49. data/spec/cql/client/connector_spec.rb +13 -3
  50. data/spec/cql/client/{asynchronous_prepared_statement_spec.rb → prepared_statement_spec.rb} +148 -1
  51. data/spec/cql/client/request_runner_spec.rb +2 -2
  52. data/spec/cql/protocol/cql_byte_buffer_spec.rb +895 -0
  53. data/spec/cql/protocol/cql_protocol_handler_spec.rb +1 -1
  54. data/spec/cql/protocol/frame_decoder_spec.rb +14 -14
  55. data/spec/cql/protocol/frame_encoder_spec.rb +7 -7
  56. data/spec/cql/protocol/requests/auth_response_request_spec.rb +4 -4
  57. data/spec/cql/protocol/requests/batch_request_spec.rb +21 -21
  58. data/spec/cql/protocol/requests/credentials_request_spec.rb +2 -2
  59. data/spec/cql/protocol/requests/execute_request_spec.rb +13 -13
  60. data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
  61. data/spec/cql/protocol/requests/prepare_request_spec.rb +2 -2
  62. data/spec/cql/protocol/requests/query_request_spec.rb +13 -13
  63. data/spec/cql/protocol/requests/register_request_spec.rb +2 -2
  64. data/spec/cql/protocol/requests/startup_request_spec.rb +4 -4
  65. data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +5 -5
  66. data/spec/cql/protocol/responses/auth_success_response_spec.rb +5 -5
  67. data/spec/cql/protocol/responses/authenticate_response_spec.rb +3 -3
  68. data/spec/cql/protocol/responses/detailed_error_response_spec.rb +15 -15
  69. data/spec/cql/protocol/responses/error_response_spec.rb +5 -5
  70. data/spec/cql/protocol/responses/event_response_spec.rb +8 -8
  71. data/spec/cql/protocol/responses/prepared_result_response_spec.rb +7 -7
  72. data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +1 -1
  73. data/spec/cql/protocol/responses/ready_response_spec.rb +2 -2
  74. data/spec/cql/protocol/responses/result_response_spec.rb +16 -16
  75. data/spec/cql/protocol/responses/rows_result_response_spec.rb +21 -21
  76. data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +3 -3
  77. data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +3 -3
  78. data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +2 -2
  79. data/spec/cql/protocol/responses/status_change_event_response_spec.rb +3 -3
  80. data/spec/cql/protocol/responses/supported_response_spec.rb +3 -3
  81. data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +3 -3
  82. data/spec/cql/protocol/responses/void_result_response_spec.rb +2 -2
  83. data/spec/cql/protocol/type_converter_spec.rb +25 -13
  84. data/spec/cql/time_uuid_spec.rb +17 -4
  85. data/spec/cql/uuid_spec.rb +5 -1
  86. data/spec/integration/protocol_spec.rb +48 -42
  87. data/spec/spec_helper.rb +0 -1
  88. metadata +27 -39
  89. data/lib/cql/byte_buffer.rb +0 -177
  90. data/lib/cql/client/synchronous_client.rb +0 -79
  91. data/lib/cql/client/synchronous_prepared_statement.rb +0 -63
  92. data/lib/cql/future.rb +0 -515
  93. data/lib/cql/io.rb +0 -15
  94. data/lib/cql/io/connection.rb +0 -220
  95. data/lib/cql/io/io_reactor.rb +0 -349
  96. data/lib/cql/protocol/decoding.rb +0 -187
  97. data/lib/cql/protocol/encoding.rb +0 -114
  98. data/spec/cql/byte_buffer_spec.rb +0 -337
  99. data/spec/cql/client/synchronous_client_spec.rb +0 -170
  100. data/spec/cql/client/synchronous_prepared_statement_spec.rb +0 -155
  101. data/spec/cql/future_spec.rb +0 -737
  102. data/spec/cql/io/connection_spec.rb +0 -484
  103. data/spec/cql/io/io_reactor_spec.rb +0 -402
  104. data/spec/cql/protocol/decoding_spec.rb +0 -547
  105. data/spec/cql/protocol/encoding_spec.rb +0 -386
  106. data/spec/integration/io_spec.rb +0 -283
  107. data/spec/support/fake_server.rb +0 -106
@@ -204,7 +204,7 @@ module Cql
204
204
 
205
205
  describe ConnectStep do
206
206
  let :step do
207
- described_class.new(io_reactor, 1111, 9, logger)
207
+ described_class.new(io_reactor, protocol_handler_factory, 1111, 9, logger)
208
208
  end
209
209
 
210
210
  let :pending_connection do
@@ -219,6 +219,14 @@ module Cql
219
219
  double(:io_reactor)
220
220
  end
221
221
 
222
+ let :protocol_handler do
223
+ double(:protocol_handler)
224
+ end
225
+
226
+ let :protocol_handler_factory do
227
+ proc { protocol_handler }
228
+ end
229
+
222
230
  let :logger do
223
231
  NullLogger.new
224
232
  end
@@ -230,8 +238,10 @@ module Cql
230
238
  describe '#run' do
231
239
  before do
232
240
  pending_connection.stub(:host).and_return('example.com')
233
- pending_connection.stub(:with_connection).and_return(new_pending_connection)
234
- io_reactor.stub(:connect).and_return(Future.resolved(connection))
241
+ pending_connection.stub(:with_connection).with(protocol_handler).and_return(new_pending_connection)
242
+ io_reactor.stub(:connect) do |_, _, _, &block|
243
+ Future.resolved(block.call(connection))
244
+ end
235
245
  end
236
246
 
237
247
  it 'connects using the connection details given' do
@@ -36,7 +36,7 @@ module Cql
36
36
  end
37
37
 
38
38
  let :raw_rows do
39
- buffer = ByteBuffer.new("\x00\x00\x00\x03")
39
+ buffer = Protocol::CqlByteBuffer.new("\x00\x00\x00\x03")
40
40
  buffer << "\x00\x00\x00\x04\x00\x00\x00\x0b"
41
41
  buffer << "\x00\x00\x00\x05hello"
42
42
  buffer << "\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -389,5 +389,152 @@ module Cql
389
389
  end
390
390
  end
391
391
  end
392
+
393
+ describe SynchronousPreparedStatement do
394
+ let :statement do
395
+ described_class.new(async_statement)
396
+ end
397
+
398
+ let :async_statement do
399
+ double(:async_statement, metadata: metadata, result_metadata: result_metadata)
400
+ end
401
+
402
+ let :metadata do
403
+ double(:metadata)
404
+ end
405
+
406
+ let :result_metadata do
407
+ double(:result_metadata)
408
+ end
409
+
410
+ let :promise do
411
+ Promise.new
412
+ end
413
+
414
+ let :future do
415
+ promise.future
416
+ end
417
+
418
+ describe '#metadata' do
419
+ it 'returns the async statement\'s metadata' do
420
+ statement.metadata.should equal(async_statement.metadata)
421
+ end
422
+ end
423
+
424
+ describe '#result_metadata' do
425
+ it 'returns the async statement\'s result metadata' do
426
+ statement.result_metadata.should equal(async_statement.result_metadata)
427
+ end
428
+ end
429
+
430
+ describe '#execute' do
431
+ it 'it calls #execute on the async statement and waits for the result' do
432
+ result = double(:result)
433
+ async_statement.should_receive(:execute).with('one', 'two', :three).and_return(future)
434
+ promise.fulfill(result)
435
+ statement.execute('one', 'two', :three).should equal(result)
436
+ end
437
+
438
+ it 'wraps AsynchronousPagedQueryResult in a synchronous wrapper' do
439
+ request = double(:request, values: ['one', 'two'])
440
+ async_result = double(:result, paging_state: 'somepagingstate')
441
+ options = {:page_size => 10}
442
+ async_statement.stub(:execute).and_return(Future.resolved(AsynchronousPreparedPagedQueryResult.new(async_statement, request, async_result, options)))
443
+ result1 = statement.execute('one', 'two', options)
444
+ result2 = result1.next_page
445
+ async_statement.should have_received(:execute).with('one', 'two', page_size: 10, paging_state: 'somepagingstate')
446
+ result2.should be_a(SynchronousPagedQueryResult)
447
+ end
448
+ end
449
+
450
+ describe '#batch' do
451
+ let :batch do
452
+ double(:batch)
453
+ end
454
+
455
+ context 'when called without a block' do
456
+ it 'delegates to the asynchronous statement and wraps the returned object in a synchronous wrapper' do
457
+ async_statement.stub(:batch).with(:unlogged, trace: true).and_return(batch)
458
+ batch.stub(:execute).and_return(Cql::Future.resolved(VoidResult.new))
459
+ b = statement.batch(:unlogged, trace: true)
460
+ b.execute.should be_a(VoidResult)
461
+ end
462
+ end
463
+
464
+ context 'when called with a block' do
465
+ it 'delegates to the asynchronous statement' do
466
+ async_statement.stub(:batch).with(:counter, trace: true).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
467
+ yielded_batch = nil
468
+ statement.batch(:counter, trace: true) { |b| yielded_batch = b }
469
+ yielded_batch.should equal(batch)
470
+ end
471
+
472
+ it 'waits for the operation to complete' do
473
+ async_statement.stub(:batch).with(:counter, anything).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
474
+ result = statement.batch(:counter) { |b| }
475
+ result.should be_a(VoidResult)
476
+ end
477
+ end
478
+ end
479
+
480
+ describe '#add_to_batch' do
481
+ it 'delegates to the async statement' do
482
+ batch = double(:batch)
483
+ connection = double(:connection)
484
+ bound_arguments = [1, 2, 3]
485
+ async_statement.stub(:add_to_batch)
486
+ statement.add_to_batch(batch, connection, bound_arguments)
487
+ async_statement.should have_received(:add_to_batch).with(batch, connection, bound_arguments)
488
+ end
489
+ end
490
+
491
+ describe '#pipeline' do
492
+ it 'executes the statement multiple times and waits for all the results' do
493
+ result1 = double(:result1)
494
+ result2 = double(:result2)
495
+ async_statement.stub(:execute).with('one', 'two', :three).and_return(Future.resolved(result1))
496
+ async_statement.stub(:execute).with('four', 'file', :all).and_return(Future.resolved(result2))
497
+ results = statement.pipeline do |p|
498
+ p.execute('one', 'two', :three)
499
+ p.execute('four', 'file', :all)
500
+ end
501
+ results.should eql([result1, result2])
502
+ end
503
+
504
+ it 'does nothing when statements are executed' do
505
+ statement.pipeline { |p| }.should == []
506
+ end
507
+ end
508
+
509
+ describe '#async' do
510
+ it 'returns an asynchronous statement' do
511
+ statement.async.should equal(async_statement)
512
+ end
513
+ end
514
+
515
+ context 'when exceptions are raised' do
516
+ it 'replaces the backtrace of the asynchronous call to make it less confusing' do
517
+ error = CqlError.new('Bork')
518
+ error.set_backtrace(['Hello', 'World'])
519
+ future.stub(:value).and_raise(error)
520
+ async_statement.stub(:execute).and_return(future)
521
+ begin
522
+ statement.execute('SELECT * FROM something')
523
+ rescue CqlError => e
524
+ e.backtrace.first.should match(%r{/prepared_statement.rb:\d+:in `execute'})
525
+ end
526
+ end
527
+
528
+ it 'does not replace the backtrace of non-CqlError errors' do
529
+ future.stub(:value).and_raise('Bork')
530
+ async_statement.stub(:execute).and_return(future)
531
+ begin
532
+ statement.execute('SELECT * FROM something')
533
+ rescue => e
534
+ e.backtrace.first.should_not match(%r{/prepared_statement.rb:\d+:in `execute'})
535
+ end
536
+ end
537
+ end
538
+ end
392
539
  end
393
540
  end
@@ -39,7 +39,7 @@ module Cql
39
39
  end
40
40
 
41
41
  let :raw_rows_response do
42
- Protocol::RawRowsResultResponse.new(2, ByteBuffer.new("\x00\x00\x00\x02\x00\x00\x00\x01a\x00\x00\x00\x01b"), nil, nil)
42
+ Protocol::RawRowsResultResponse.new(2, Protocol::CqlByteBuffer.new("\x00\x00\x00\x02\x00\x00\x00\x01a\x00\x00\x00\x01b"), nil, nil)
43
43
  end
44
44
 
45
45
  let :void_response do
@@ -179,7 +179,7 @@ module Cql
179
179
 
180
180
  it 'returns a LazyQueryResult that knows its paging state' do
181
181
  metadata = [['ks', 'tbl', 'col', :varchar]]
182
- connection.stub(:send_request).with(request, anything).and_return(Future.resolved(Protocol::RawRowsResultResponse.new(2, ByteBuffer.new("\x00\x00\x00\x02\x00\x00\x00\x01a\x00\x00\x00\x01b"), 'bazbuzz', nil)))
182
+ connection.stub(:send_request).with(request, anything).and_return(Future.resolved(Protocol::RawRowsResultResponse.new(2, Protocol::CqlByteBuffer.new("\x00\x00\x00\x02\x00\x00\x00\x01a\x00\x00\x00\x01b"), 'bazbuzz', nil)))
183
183
  response = runner.execute(connection, request, nil, metadata).value
184
184
  response.paging_state.should == 'bazbuzz'
185
185
  end
@@ -0,0 +1,895 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Protocol
8
+ describe CqlByteBuffer do
9
+ let :buffer do
10
+ described_class.new
11
+ end
12
+
13
+ describe '#read_unsigned_byte' do
14
+ let :buffer do
15
+ described_class.new("\xab")
16
+ end
17
+
18
+ it 'decodes a raw byte' do
19
+ buffer.read_unsigned_byte.should == 0xab
20
+ end
21
+
22
+ it 'consumes the byte' do
23
+ buffer.read_unsigned_byte
24
+ buffer.should be_empty
25
+ end
26
+
27
+ it 'raises an error when there is no byte available' do
28
+ expect { described_class.new.read_unsigned_byte }.to raise_error(DecodingError)
29
+ end
30
+ end
31
+
32
+ describe '#read_varint' do
33
+ it 'decodes a variable length integer' do
34
+ buffer = described_class.new("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[")
35
+ buffer.read_varint(17).should == 1231312312331283012830129382342342412123
36
+ end
37
+
38
+ it 'decodes a negative variable length integer' do
39
+ buffer = described_class.new("\xC9v\x8D:\x86")
40
+ buffer.read_varint(5).should == -234234234234
41
+ end
42
+
43
+ it 'decodes an unsigned variable length integer' do
44
+ buffer = described_class.new("\xC9v\x8D:\x86")
45
+ buffer.read_varint(5, false).should == 865277393542
46
+ end
47
+
48
+ it 'consumes the bytes' do
49
+ buffer = described_class.new("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[\x01\x02\x03")
50
+ buffer.read_varint(17)
51
+ buffer.should eql_bytes("\x01\x02\x03")
52
+ end
53
+
54
+ it 'raises an error when there is not enough bytes available' do
55
+ buffer = described_class.new("\xC9v\x8D:")
56
+ expect { buffer.read_varint(7) }.to raise_error(DecodingError)
57
+ end
58
+ end
59
+
60
+ describe '#read_decimal' do
61
+ let :buffer do
62
+ described_class.new("\x00\x00\x00\x12\r'\xFDI\xAD\x80f\x11g\xDCfV\xAA")
63
+ end
64
+
65
+ it 'decodes a decimal to a BigDecimal' do
66
+ buffer.read_decimal.should == BigDecimal.new('1042342234234.123423435647768234')
67
+ end
68
+
69
+ it 'decodes a negative decimal' do
70
+ buffer = described_class.new("\x00\x00\x00\x12\xF2\xD8\x02\xB6R\x7F\x99\xEE\x98#\x99\xA9V")
71
+ buffer.read_decimal.should == BigDecimal.new('-1042342234234.123423435647768234')
72
+ end
73
+
74
+ it 'decodes a positive decimal with only fractions' do
75
+ buffer = described_class.new("\x00\x00\x00\x13*\xF8\xC4\xDF\xEB]o")
76
+ buffer.read_decimal.should == BigDecimal.new('0.0012095473475870063')
77
+ end
78
+
79
+ it 'decodes a negative decimal with only fractions' do
80
+ buffer = described_class.new("\x00\x00\x00\x13\xD5\a;\x20\x14\xA2\x91")
81
+ buffer.read_decimal.should == BigDecimal.new('-0.0012095473475870063')
82
+ end
83
+
84
+ it 'consumes the bytes' do
85
+ buffer << 'HELLO'
86
+ buffer.read_decimal(buffer.length - 5)
87
+ buffer.should eql_bytes('HELLO')
88
+ end
89
+
90
+ it 'defaults to using the buffer length' do
91
+ b1 = buffer
92
+ b2 = buffer.dup
93
+ b1.read_decimal.should == b2.read_decimal(b2.length)
94
+ end
95
+
96
+ it 'raises an error when there is not enough bytes available' do
97
+ b = described_class.new(buffer.read(3))
98
+ expect { b.read_decimal(7) }.to raise_error(DecodingError)
99
+ end
100
+ end
101
+
102
+ describe '#read_long' do
103
+ it 'decodes a positive long' do
104
+ buffer = described_class.new("\x00\x00\xca\xfe\xba\xbe\x00\x00")
105
+ buffer.read_long.should == 0x0000cafebabe0000
106
+ end
107
+
108
+ it 'decodes a negative long' do
109
+ buffer = described_class.new("\xff\xee\xdd\xcc\xbb\xaa\x99\x88")
110
+ buffer.read_long.should == 0xffeeddccbbaa9988 - 0x10000000000000000
111
+ end
112
+
113
+ it 'consumes the bytes' do
114
+ buffer = described_class.new("\xca\xfe\xba\xbe\xca\xfe\xba\xbe\xca\xfe\xba\xbe")
115
+ buffer.read_long
116
+ buffer.should eql_bytes("\xca\xfe\xba\xbe")
117
+ end
118
+
119
+ it 'raises an error when there is not enough bytes available' do
120
+ b = described_class.new("\xca\xfe\xba\xbe\x00")
121
+ expect { b.read_long }.to raise_error(DecodingError)
122
+ end
123
+ end
124
+
125
+ describe '#read_double' do
126
+ it 'decodes a double' do
127
+ buffer = described_class.new("@\xC3\x88\x0F\xC2\x7F\x9DU")
128
+ buffer.read_double.should == 10000.123123123
129
+ end
130
+
131
+ it 'consumes the bytes' do
132
+ buffer = described_class.new("@\xC3\x88\x0F\xC2\x7F\x9DUxyz")
133
+ buffer.read_double
134
+ buffer.should eql_bytes('xyz')
135
+ end
136
+
137
+ it 'raises an error when there is not enough bytes available' do
138
+ buffer = described_class.new("@\xC3\x88\x0F")
139
+ expect { buffer.read_double }.to raise_error(DecodingError)
140
+ end
141
+ end
142
+
143
+ describe '#read_float' do
144
+ it 'decodes a float' do
145
+ buffer = described_class.new("AB\x14{")
146
+ buffer.read_float.should be_within(0.00001).of(12.13)
147
+ end
148
+
149
+ it 'consumes the bytes' do
150
+ buffer = described_class.new("AB\x14{xyz")
151
+ buffer.read_float
152
+ buffer.should eql_bytes('xyz')
153
+ end
154
+
155
+ it 'raises an error when there is not enough bytes available' do
156
+ buffer = described_class.new("\x0F")
157
+ expect { buffer.read_float }.to raise_error(DecodingError)
158
+ end
159
+ end
160
+
161
+ describe '#read_signed_int' do
162
+ let :buffer do
163
+ described_class.new("\x00\xff\x00\xff")
164
+ end
165
+
166
+ it 'decodes a positive int' do
167
+ buffer.read_signed_int.should == 0x00ff00ff
168
+ end
169
+
170
+ it 'decodes a negative int' do
171
+ buffer = described_class.new("\xff\xee\xdd\xcc")
172
+ buffer.read_signed_int.should == 0xffeeddcc - 0x100000000
173
+ end
174
+
175
+ it 'consumes the bytes' do
176
+ buffer << "\xab\xcd"
177
+ buffer.read_signed_int
178
+ buffer.should eql_bytes("\xab\xcd")
179
+ end
180
+
181
+ it 'raises an error when there are not enough bytes in the buffer' do
182
+ buffer = described_class.new("\x01\xab")
183
+ expect { buffer.read_signed_int }.to raise_error(DecodingError)
184
+ end
185
+ end
186
+
187
+ describe '#read_unsigned_short' do
188
+ let :buffer do
189
+ described_class.new("\x00\x02")
190
+ end
191
+
192
+ it 'decodes a short' do
193
+ buffer.read_unsigned_short.should == 2
194
+ end
195
+
196
+ it 'consumes the bytes' do
197
+ buffer << "\xff\xff"
198
+ buffer.read_unsigned_short
199
+ buffer.should eql_bytes("\xff\xff")
200
+ end
201
+
202
+ it 'raises an error when there are not enough bytes in the buffer' do
203
+ buffer = described_class.new("\x01")
204
+ expect { buffer.read_unsigned_short }.to raise_error(DecodingError)
205
+ end
206
+ end
207
+
208
+ describe '#read_string' do
209
+ let :buffer do
210
+ described_class.new("\x00\x0bhej och hå")
211
+ end
212
+
213
+ it 'decodes a string' do
214
+ buffer.read_string.should == 'hej och hå'.force_encoding(::Encoding::UTF_8)
215
+ end
216
+
217
+ it 'decodes a string as UTF-8' do
218
+ buffer.read_string.encoding.should == ::Encoding::UTF_8
219
+ end
220
+
221
+ it 'decodes an empty string' do
222
+ buffer = described_class.new("\x00\x00")
223
+ buffer.read_string.should be_empty
224
+ end
225
+
226
+ it 'consumes the bytes' do
227
+ buffer << "\xff\xff"
228
+ buffer.read_string
229
+ buffer.should eql_bytes("\xff\xff")
230
+ end
231
+
232
+ it 'raises an error when there are not enough bytes in the buffer' do
233
+ b = described_class.new(buffer.read(5))
234
+ expect { b.read_string }.to raise_error(DecodingError)
235
+ end
236
+ end
237
+
238
+ describe '#read_long_string' do
239
+ let :buffer do
240
+ described_class.new("\x00\x01\x00\00" << ('x' * 0x10000))
241
+ end
242
+
243
+ it 'decodes a string' do
244
+ str = buffer.read_long_string
245
+ str.should start_with('xxx')
246
+ str.length.should == 0x10000
247
+ end
248
+
249
+ it 'decodes a string as UTF-8' do
250
+ buffer.read_long_string.encoding.should == ::Encoding::UTF_8
251
+ end
252
+
253
+ it 'consumes the bytes' do
254
+ buffer << "\xff\xff"
255
+ buffer.read_long_string
256
+ buffer.should eql_bytes("\xff\xff")
257
+ end
258
+
259
+ it 'raises an error when there are not enough bytes in the buffer' do
260
+ b = described_class.new(buffer.read(246))
261
+ expect { b.read_long_string }.to raise_error(DecodingError)
262
+ end
263
+ end
264
+
265
+ describe '#read_uuid' do
266
+ let :buffer do
267
+ described_class.new("\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11")
268
+ end
269
+
270
+ it 'decodes a UUID as a Cql::Uuid' do
271
+ buffer.read_uuid.should == Uuid.new('a4a70900-24e1-11df-8924-001ff3591711')
272
+ end
273
+
274
+ it 'decodes a UUID as a Cql::TimeUuid' do
275
+ uuid = buffer.read_uuid(TimeUuid)
276
+ uuid.should == TimeUuid.new('a4a70900-24e1-11df-8924-001ff3591711')
277
+ uuid.should be_a(TimeUuid)
278
+ end
279
+
280
+ it 'consumes the bytes' do
281
+ buffer.read_uuid
282
+ buffer.should be_empty
283
+ end
284
+
285
+ it 'raises an error when there a not enough bytes in the buffer' do
286
+ b = described_class.new(buffer.discard(2).read(5))
287
+ expect { b.read_uuid }.to raise_error(DecodingError)
288
+ end
289
+ end
290
+
291
+ describe '#read_string_list' do
292
+ let :buffer do
293
+ described_class.new("\x00\x02\x00\x05hello\x00\x05world")
294
+ end
295
+
296
+ it 'decodes a string list' do
297
+ buffer.read_string_list.should == %w[hello world]
298
+ end
299
+
300
+ it 'decodes an empty string list' do
301
+ buffer = described_class.new("\x00\x00")
302
+ buffer.read_string_list.should == []
303
+ end
304
+
305
+ it 'consumes the bytes' do
306
+ buffer << "\xff\xff"
307
+ buffer.read_string_list
308
+ buffer.should eql_bytes("\xff\xff")
309
+ end
310
+
311
+ it 'raises an error when there are not enough bytes in the buffer' do
312
+ b = described_class.new(buffer.read(13))
313
+ expect { b.read_string_list }.to raise_error(DecodingError)
314
+ end
315
+ end
316
+
317
+ describe '#read_bytes' do
318
+ let :buffer do
319
+ described_class.new("\x00\x01\x00\x00" << ("\x42" * 0x10000))
320
+ end
321
+
322
+ it 'decodes a byte array' do
323
+ buffer.read_bytes.should eql_bytes("\x42" * 0x10000)
324
+ end
325
+
326
+ it 'decodes an empty byte array' do
327
+ buffer = described_class.new("\x00\x00\x00\x00")
328
+ buffer.read_bytes.should be_empty
329
+ end
330
+
331
+ it 'decodes null' do
332
+ buffer = described_class.new("\x80\x00\x00\x00")
333
+ buffer.read_bytes.should be_nil
334
+ end
335
+
336
+ it 'consumes the bytes' do
337
+ buffer << "\xab\xcd"
338
+ buffer.read_bytes
339
+ buffer.should eql_bytes("\xab\xcd")
340
+ end
341
+
342
+ it 'raises an error when there are not enough bytes in the buffer' do
343
+ b = described_class.new(buffer.read(10))
344
+ expect { b.read_bytes }.to raise_error(DecodingError)
345
+ end
346
+ end
347
+
348
+ describe '#read_short_bytes' do
349
+ let :buffer do
350
+ described_class.new("\x01\x00" << ("\x42" * 0x100))
351
+ end
352
+
353
+ it 'decodes a byte array' do
354
+ buffer.read_short_bytes.should eql_bytes("\x42" * 0x100)
355
+ end
356
+
357
+ it 'decodes an empty byte array' do
358
+ buffer = described_class.new("\x00\x00\x00\x00")
359
+ buffer.read_short_bytes.should be_empty
360
+ end
361
+
362
+ it 'decodes null' do
363
+ buffer = described_class.new("\x80\x00")
364
+ buffer.read_short_bytes.should be_nil
365
+ end
366
+
367
+ it 'consumes the bytes' do
368
+ buffer << "\xab\xcd"
369
+ buffer.read_short_bytes
370
+ buffer.should eql_bytes("\xab\xcd")
371
+ end
372
+
373
+ it 'raises an error when there are not enough bytes in the buffer' do
374
+ b = described_class.new(buffer.read(10))
375
+ expect { b.read_short_bytes }.to raise_error(DecodingError)
376
+ end
377
+ end
378
+
379
+ describe '#read_option' do
380
+ it 'decodes an option ID and value with instructions from a block' do
381
+ buffer = described_class.new("\x00\x01\x00\x03foo")
382
+ id, value = buffer.read_option do |id, buffer|
383
+ buffer.read_string
384
+ end
385
+ id.should == 1
386
+ value.should == 'foo'
387
+ end
388
+
389
+ it 'decodes an option ID and nil value when there is no block' do
390
+ buffer = described_class.new("\xaa\xbb")
391
+ id, value = buffer.read_option
392
+ id.should == 0xaabb
393
+ value.should be_nil
394
+ end
395
+
396
+ it 'consumes the bytes' do
397
+ buffer = described_class.new("\x00\x01\x00\x03\xab")
398
+ id, value = buffer.read_option do |id, buffer|
399
+ buffer.read_short
400
+ end
401
+ buffer.should eql_bytes("\xab")
402
+ end
403
+
404
+ it 'raises an error when there are not enough bytes in the buffer' do
405
+ b = described_class.new("\xaa")
406
+ expect { b.read_option }.to raise_error(DecodingError)
407
+ end
408
+ end
409
+
410
+ describe '#read_inet' do
411
+ it 'decodes an IPv4 + port pair' do
412
+ buffer = described_class.new("\x04\x00\x00\x00\x00\x00\x00#R")
413
+ ip_addr, port = buffer.read_inet
414
+ ip_addr.should == IPAddr.new('0.0.0.0')
415
+ port.should == 9042
416
+ end
417
+
418
+ it 'decodes an IPv6 + port pair' do
419
+ buffer = described_class.new("\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00#R")
420
+ ip_addr, port = buffer.read_inet
421
+ ip_addr.should == IPAddr.new('::1')
422
+ port.should == 9042
423
+ end
424
+
425
+ it 'consumes the bytes' do
426
+ buffer = described_class.new("\x04\x00\x00\x00\x00\x00\x00#R\xff\xaa")
427
+ buffer.read_inet
428
+ buffer.should eql_bytes("\xff\xaa")
429
+ end
430
+
431
+ it 'raises an error when there are not enough bytes in the buffer' do
432
+ buffer1 = described_class.new("\x04\x00\x00\x00\x00\x00\x00")
433
+ expect { buffer1.read_inet }.to raise_error(DecodingError)
434
+ buffer2 = described_class.new("\x04\x00\x00\x00")
435
+ expect { buffer2.read_inet }.to raise_error(DecodingError)
436
+ end
437
+ end
438
+
439
+ describe '#read_consistency' do
440
+ {
441
+ :any => "\x00\x00",
442
+ :one => "\x00\x01",
443
+ :two => "\x00\x02",
444
+ :three => "\x00\x03",
445
+ :quorum => "\x00\x04",
446
+ :all => "\x00\x05",
447
+ :local_quorum => "\x00\x06",
448
+ :each_quorum => "\x00\x07",
449
+ :serial => "\x00\x08",
450
+ :local_serial => "\x00\x09",
451
+ :local_one => "\x00\x0a",
452
+ }.each do |consistency, bytes|
453
+ it "decodes #{consistency.to_s.upcase}" do
454
+ buffer = described_class.new(bytes)
455
+ buffer.read_consistency.should == consistency
456
+ end
457
+ end
458
+
459
+ it 'raises an exception for an unknown consistency' do
460
+ expect { CqlByteBuffer.new("\xff\xff").read_consistency }.to raise_error(DecodingError)
461
+ expect { CqlByteBuffer.new("\x00\x0f").read_consistency }.to raise_error(DecodingError)
462
+ end
463
+ end
464
+
465
+ describe '#read_string_map' do
466
+ let :buffer do
467
+ described_class.new("\x00\x02\x00\x05hello\x00\x05world\x00\x03foo\x00\x03bar")
468
+ end
469
+
470
+ it 'decodes a string multimap' do
471
+ buffer.read_string_map.should == {'hello' => 'world', 'foo' => 'bar'}
472
+ end
473
+
474
+ it 'decodes an empty string map' do
475
+ buffer = described_class.new("\x00\x00")
476
+ buffer.read_string_map.should == {}
477
+ end
478
+
479
+ it 'consumes the bytes' do
480
+ buffer << "\xff"
481
+ buffer.read_string_map
482
+ buffer.should eql_bytes("\xff")
483
+ end
484
+
485
+ it 'raises an error when there are not enough bytes in the buffer' do
486
+ b = described_class.new(buffer.read(20))
487
+ expect { b.read_string_map }.to raise_error(DecodingError)
488
+ end
489
+ end
490
+
491
+ describe '#read_string_multimap' do
492
+ let :buffer do
493
+ described_class.new("\x00\x02\x00\x0bCQL_VERSION\x00\x01\x00\x053.0.0\x00\x0bCOMPRESSION\x00\x02\x00\x06snappy\x00\x04gzip")
494
+ end
495
+
496
+ it 'decodes a string multimap' do
497
+ buffer.read_string_multimap.should == {'CQL_VERSION' => ['3.0.0'], 'COMPRESSION' => ['snappy', 'gzip']}
498
+ end
499
+
500
+ it 'decodes an empty string multimap' do
501
+ buffer = described_class.new("\x00\x00")
502
+ buffer.read_string_multimap.should == {}
503
+ end
504
+
505
+ it 'consumes the bytes' do
506
+ buffer << "\xff"
507
+ buffer.read_string_multimap
508
+ buffer.should eql_bytes("\xff")
509
+ end
510
+
511
+ it 'raises an error when there are not enough bytes in the buffer' do
512
+ b = described_class.new(buffer.read(40))
513
+ expect { b.read_string_multimap }.to raise_error(DecodingError)
514
+ end
515
+ end
516
+
517
+ describe '#append_int' do
518
+ it 'encodes an int' do
519
+ buffer.append_int(2323234234)
520
+ buffer.should eql_bytes("\x8a\x79\xbd\xba")
521
+ end
522
+
523
+ it 'appends to the buffer' do
524
+ buffer << "\xab"
525
+ buffer.append_int(10)
526
+ buffer.should eql_bytes("\xab\x00\x00\x00\x0a")
527
+ end
528
+
529
+ it 'returns the buffer' do
530
+ result = buffer.append_int(2323234234)
531
+ result.should equal(buffer)
532
+ end
533
+ end
534
+
535
+ describe '#append_short' do
536
+ it 'encodes a short' do
537
+ buffer.append_short(0xabcd)
538
+ buffer.should eql_bytes("\xab\xcd")
539
+ end
540
+
541
+ it 'appends to the buffer' do
542
+ buffer << "\xab"
543
+ buffer.append_short(10)
544
+ buffer.should eql_bytes("\xab\x00\x0a")
545
+ end
546
+
547
+ it 'returns the buffer' do
548
+ result = buffer.append_short(42)
549
+ result.should equal(buffer)
550
+ end
551
+ end
552
+
553
+ describe '#append_string' do
554
+ it 'encodes a string' do
555
+ buffer.append_string('hello')
556
+ buffer.should eql_bytes("\x00\x05hello")
557
+ end
558
+
559
+ it 'encodes a string with multibyte characters' do
560
+ buffer << "\xff"
561
+ str = 'I love π'
562
+ buffer.append_string(str)
563
+ buffer.should eql_bytes("\xff\x00\x09I love π")
564
+ end
565
+
566
+ it 'encodes an empty string' do
567
+ buffer.append_string('')
568
+ buffer.should eql_bytes("\x00\x00")
569
+ end
570
+
571
+ it 'encodes a non-string' do
572
+ buffer.append_string(42)
573
+ buffer.should eql_bytes("\x00\x0242")
574
+ end
575
+
576
+ it 'appends to the buffer' do
577
+ buffer << "\xab"
578
+ buffer.append_string('foo')
579
+ buffer.should eql_bytes("\xab\x00\x03foo")
580
+ end
581
+
582
+ it 'returns the buffer' do
583
+ result = buffer.append_string('hello')
584
+ result.should equal(buffer)
585
+ end
586
+ end
587
+
588
+ describe '#append_long_string' do
589
+ it 'encodes a string' do
590
+ buffer.append_long_string('hello world ' * 100_000)
591
+ buffer.read(45).should eql_bytes("\x00\x12\x4f\x80hello world hello world hello world hello")
592
+ end
593
+
594
+ it 'encodes a string with multibyte characters' do
595
+ buffer << "\xff"
596
+ str = 'I love π'
597
+ buffer.append_long_string(str)
598
+ buffer.should eql_bytes("\xff\x00\x00\x00\x09I love π")
599
+ end
600
+
601
+ it 'encodes an empty string' do
602
+ buffer.append_long_string('')
603
+ buffer.should eql_bytes("\x00\x00\x00\x00")
604
+ end
605
+
606
+ it 'appends to the buffer' do
607
+ buffer << "\xab"
608
+ buffer.append_long_string('foo')
609
+ buffer.should eql_bytes("\xab\x00\x00\x00\x03foo")
610
+ end
611
+
612
+ it 'returns the buffer' do
613
+ result = buffer.append_long_string('hello')
614
+ result.should equal(buffer)
615
+ end
616
+ end
617
+
618
+ describe '#append_uuid' do
619
+ let :uuid do
620
+ Uuid.new('a4a70900-24e1-11df-8924-001ff3591711')
621
+ end
622
+
623
+ it 'encodes an UUID' do
624
+ buffer.append_uuid(uuid)
625
+ buffer.should eql_bytes("\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11")
626
+ end
627
+
628
+ it 'encodes a UUID as 16 bytes' do
629
+ buffer.append_uuid(Uuid.new('00000000-24e1-11df-8924-001ff3591711'))
630
+ buffer.size.should eql(16)
631
+ end
632
+
633
+ it 'appends to the buffer' do
634
+ buffer << 'FOO'
635
+ buffer.append_uuid(uuid)
636
+ buffer.read(3).should eql_bytes('FOO')
637
+ end
638
+
639
+ it 'returns the buffer' do
640
+ result = buffer.append_uuid(uuid)
641
+ result.should equal(buffer)
642
+ end
643
+ end
644
+
645
+ describe '#append_string_list' do
646
+ it 'encodes a string list' do
647
+ buffer.append_string_list(%w[foo bar hello world])
648
+ buffer.should eql_bytes("\x00\x04\x00\x03foo\x00\x03bar\x00\x05hello\x00\x05world")
649
+ end
650
+
651
+ it 'encodes a string with multibyte characters' do
652
+ buffer << "\xff"
653
+ str = %w[I love π]
654
+ buffer.append_string_list(str)
655
+ buffer.should eql_bytes("\xff\x00\x03\x00\x01I\x00\x04love\x00\x02π")
656
+ end
657
+
658
+ it 'encodes an empty string list' do
659
+ buffer.append_string_list([])
660
+ buffer.should eql_bytes("\x00\x00")
661
+ end
662
+
663
+ it 'appends to the buffer' do
664
+ buffer << "\xab"
665
+ buffer.append_string_list(%w[foo bar])
666
+ buffer.should eql_bytes("\xab\x00\x02\x00\x03foo\x00\x03bar")
667
+ end
668
+
669
+ it 'returns the buffer' do
670
+ result = buffer.append_string_list(%w[foo])
671
+ result.should equal(buffer)
672
+ end
673
+ end
674
+
675
+ describe '#append_bytes' do
676
+ it 'encodes a byte array' do
677
+ buffer.append_bytes("\xaa" * 2000)
678
+ buffer.should eql_bytes("\x00\x00\x07\xd0" << ("\xaa" * 2000))
679
+ end
680
+
681
+ it 'encodes a string with multibyte characters' do
682
+ buffer << "\xff"
683
+ str = 'I love π'
684
+ buffer.append_bytes(str)
685
+ buffer.should eql_bytes("\xff\x00\x00\x00\x09I love π")
686
+ end
687
+
688
+ it 'encodes nil' do
689
+ buffer.append_bytes(nil)
690
+ buffer.should eql_bytes("\xff\xff\xff\xff")
691
+ end
692
+
693
+ it 'appends to the buffer' do
694
+ buffer << "\xab"
695
+ buffer.append_bytes("\xf0\x0b\xbar")
696
+ buffer.should eql_bytes("\xab\x00\x00\x00\x04\xf0\x0b\xbar")
697
+ end
698
+
699
+ it 'returns the buffer' do
700
+ result = buffer.append_bytes("\xab")
701
+ result.should equal(buffer)
702
+ end
703
+ end
704
+
705
+ describe '#append_short_bytes' do
706
+ it 'encodes a byte array' do
707
+ buffer.append_short_bytes("\xaa\xbb\xcc")
708
+ buffer.should eql_bytes("\x00\x03\xaa\xbb\xcc")
709
+ end
710
+
711
+ it 'encodes a string with multibyte characters' do
712
+ buffer << "\xff"
713
+ str = 'I love π'
714
+ buffer.append_short_bytes(str)
715
+ buffer.should eql_bytes("\xff\x00\x09I love π")
716
+ end
717
+
718
+ it 'encodes nil' do
719
+ buffer.append_short_bytes(nil)
720
+ buffer.should eql_bytes("\xff\xff")
721
+ end
722
+
723
+ it 'appends to the buffer' do
724
+ buffer << "\xab"
725
+ buffer.append_short_bytes("\xf0\x0b\xbar")
726
+ buffer.should eql_bytes("\xab\x00\x04\xf0\x0b\xbar")
727
+ end
728
+
729
+ it 'returns the buffer' do
730
+ result = buffer.append_short_bytes("\xab")
731
+ result.should equal(buffer)
732
+ end
733
+ end
734
+
735
+ describe '#append_consistency' do
736
+ {
737
+ :any => "\x00\x00",
738
+ :one => "\x00\x01",
739
+ :two => "\x00\x02",
740
+ :three => "\x00\x03",
741
+ :quorum => "\x00\x04",
742
+ :all => "\x00\x05",
743
+ :local_quorum => "\x00\x06",
744
+ :each_quorum => "\x00\x07",
745
+ :serial => "\x00\x08",
746
+ :local_serial => "\x00\x09",
747
+ :local_one => "\x00\x0a",
748
+ }.each do |consistency, expected_encoding|
749
+ it "encodes #{consistency}" do
750
+ buffer.append_consistency(consistency)
751
+ buffer.should eql_bytes(expected_encoding)
752
+ end
753
+ end
754
+
755
+ it 'raises an exception for an unknown consistency' do
756
+ expect { buffer.append_consistency(:foo) }.to raise_error(EncodingError)
757
+ end
758
+
759
+ it 'appends to the buffer' do
760
+ buffer << "\xab"
761
+ buffer.append_consistency(:one)
762
+ buffer.should eql_bytes("\xab\x00\x01")
763
+ end
764
+
765
+ it 'returns the buffer' do
766
+ result = buffer.append_consistency(:quorum)
767
+ result.should equal(buffer)
768
+ end
769
+ end
770
+
771
+ describe '#append_string_map' do
772
+ it 'encodes a string map' do
773
+ buffer.append_string_map('HELLO' => 'world', 'foo' => 'bar')
774
+ buffer.should eql_bytes("\x00\x02\x00\x05HELLO\x00\x05world\x00\x03foo\x00\x03bar")
775
+ end
776
+
777
+ it 'encodes an empty map' do
778
+ buffer.append_string_map({})
779
+ buffer.should eql_bytes("\x00\x00")
780
+ end
781
+
782
+ it 'appends to the buffer' do
783
+ buffer << "\xab"
784
+ buffer.append_string_map('foo' => 'bar')
785
+ buffer.should eql_bytes("\xab\x00\x01\x00\x03foo\x00\x03bar")
786
+ end
787
+
788
+ it 'returns the buffer' do
789
+ result = buffer.append_string_map('HELLO' => 'world')
790
+ result.should equal(buffer)
791
+ end
792
+ end
793
+
794
+ describe '#append_long' do
795
+ it 'encodes a long' do
796
+ buffer.append_long(0x0123456789)
797
+ buffer.should eql_bytes("\x00\x00\x00\x01\x23\x45\x67\x89")
798
+ end
799
+
800
+ it 'appends to the buffer' do
801
+ buffer << "\x99"
802
+ buffer.append_long(0x0123456789)
803
+ buffer.should eql_bytes("\x99\x00\x00\x00\x01\x23\x45\x67\x89")
804
+ end
805
+
806
+ it 'returns the buffer' do
807
+ result = buffer.append_long(1)
808
+ result.should equal(buffer)
809
+ end
810
+ end
811
+
812
+ describe '#append_varint' do
813
+ it 'encodes a variable length integer' do
814
+ buffer.append_varint(1231312312331283012830129382342342412123)
815
+ buffer.should eql_bytes("\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a[")
816
+ end
817
+
818
+ it 'encodes a negative variable length integer' do
819
+ buffer.append_varint(-234234234234)
820
+ buffer.should eql_bytes("\xC9v\x8D:\x86")
821
+ end
822
+
823
+ it 'encodes a negative variable length integer' do
824
+ buffer.append_varint(-1)
825
+ buffer.should eql_bytes("\xff")
826
+ end
827
+
828
+ it 'appends to the buffer' do
829
+ buffer << "\x99"
830
+ buffer.append_varint(-234234234234)
831
+ buffer.should eql_bytes("\x99\xC9v\x8D:\x86")
832
+ end
833
+
834
+ it 'returns the buffer' do
835
+ result = buffer.append_varint(-234234234234)
836
+ result.should equal(buffer)
837
+ end
838
+ end
839
+
840
+ describe '#append_decimal' do
841
+ it 'encodes a BigDecimal as a decimal' do
842
+ buffer.append_decimal(BigDecimal.new('1042342234234.123423435647768234'))
843
+ buffer.should eql_bytes("\x00\x00\x00\x12\r'\xFDI\xAD\x80f\x11g\xDCfV\xAA")
844
+ end
845
+
846
+ it 'appends to the buffer' do
847
+ buffer << "\x99"
848
+ buffer.append_decimal(BigDecimal.new('1042342234234.123423435647768234'))
849
+ buffer.read(1).should eql_bytes("\x99")
850
+ end
851
+
852
+ it 'returns the buffer' do
853
+ result = buffer.append_decimal(BigDecimal.new('3.14'))
854
+ result.should equal(buffer)
855
+ end
856
+ end
857
+
858
+ describe '#append_double' do
859
+ it 'encodes a double' do
860
+ buffer.append_double(10000.123123123)
861
+ buffer.should eql_bytes("@\xC3\x88\x0F\xC2\x7F\x9DU")
862
+ end
863
+
864
+ it 'appends to the buffer' do
865
+ buffer << 'BEFORE'
866
+ buffer.append_double(10000.123123123)
867
+ buffer.read(6).should eql_bytes('BEFORE')
868
+ end
869
+
870
+ it 'returns the buffer' do
871
+ result = buffer.append_double(10000.123123123)
872
+ result.should equal(buffer)
873
+ end
874
+ end
875
+
876
+ describe '#append_float' do
877
+ it 'encodes a float' do
878
+ buffer.append_float(12.13)
879
+ buffer.should eql_bytes("AB\x14{")
880
+ end
881
+
882
+ it 'appends to the buffer' do
883
+ buffer << 'BEFORE'
884
+ buffer.append_float(12.13)
885
+ buffer.read(6).should eql_bytes('BEFORE')
886
+ end
887
+
888
+ it 'returns the buffer' do
889
+ result = buffer.append_float(12.13)
890
+ result.should equal(buffer)
891
+ end
892
+ end
893
+ end
894
+ end
895
+ end