cql-rb 1.2.2 → 2.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/README.md +139 -17
  4. data/lib/cql/client.rb +237 -8
  5. data/lib/cql/client/asynchronous_client.rb +138 -54
  6. data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
  7. data/lib/cql/client/authenticators.rb +46 -0
  8. data/lib/cql/client/batch.rb +115 -0
  9. data/lib/cql/client/connector.rb +255 -0
  10. data/lib/cql/client/execute_options_decoder.rb +25 -9
  11. data/lib/cql/client/keyspace_changer.rb +5 -5
  12. data/lib/cql/client/peer_discovery.rb +33 -0
  13. data/lib/cql/client/query_result.rb +124 -1
  14. data/lib/cql/client/request_runner.rb +4 -2
  15. data/lib/cql/client/synchronous_client.rb +14 -2
  16. data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
  17. data/lib/cql/future.rb +97 -50
  18. data/lib/cql/io/connection.rb +0 -1
  19. data/lib/cql/io/io_reactor.rb +1 -1
  20. data/lib/cql/protocol.rb +8 -1
  21. data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
  22. data/lib/cql/protocol/decoding.rb +10 -15
  23. data/lib/cql/protocol/frame_decoder.rb +2 -1
  24. data/lib/cql/protocol/frame_encoder.rb +5 -4
  25. data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
  26. data/lib/cql/protocol/requests/batch_request.rb +59 -0
  27. data/lib/cql/protocol/requests/credentials_request.rb +1 -1
  28. data/lib/cql/protocol/requests/execute_request.rb +45 -17
  29. data/lib/cql/protocol/requests/options_request.rb +1 -1
  30. data/lib/cql/protocol/requests/prepare_request.rb +1 -1
  31. data/lib/cql/protocol/requests/query_request.rb +97 -5
  32. data/lib/cql/protocol/requests/register_request.rb +1 -1
  33. data/lib/cql/protocol/requests/startup_request.rb +4 -4
  34. data/lib/cql/protocol/response.rb +2 -2
  35. data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
  36. data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
  37. data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
  38. data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
  39. data/lib/cql/protocol/responses/error_response.rb +3 -2
  40. data/lib/cql/protocol/responses/event_response.rb +3 -2
  41. data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
  42. data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
  43. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  44. data/lib/cql/protocol/responses/result_response.rb +2 -2
  45. data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
  46. data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
  47. data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
  48. data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
  49. data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
  50. data/lib/cql/protocol/responses/supported_response.rb +1 -1
  51. data/lib/cql/protocol/responses/void_result_response.rb +1 -1
  52. data/lib/cql/protocol/type_converter.rb +2 -2
  53. data/lib/cql/uuid.rb +2 -2
  54. data/lib/cql/version.rb +1 -1
  55. data/spec/cql/client/asynchronous_client_spec.rb +493 -50
  56. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
  57. data/spec/cql/client/authenticators_spec.rb +56 -0
  58. data/spec/cql/client/batch_spec.rb +277 -0
  59. data/spec/cql/client/connector_spec.rb +606 -0
  60. data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
  61. data/spec/cql/client/keyspace_changer_spec.rb +8 -8
  62. data/spec/cql/client/peer_discovery_spec.rb +92 -0
  63. data/spec/cql/client/query_result_spec.rb +352 -0
  64. data/spec/cql/client/request_runner_spec.rb +31 -5
  65. data/spec/cql/client/synchronous_client_spec.rb +44 -1
  66. data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
  67. data/spec/cql/future_spec.rb +50 -2
  68. data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
  69. data/spec/cql/protocol/decoding_spec.rb +16 -6
  70. data/spec/cql/protocol/encoding_spec.rb +3 -1
  71. data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
  72. data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
  73. data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
  74. data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
  75. data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
  76. data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
  77. data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
  78. data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
  79. data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
  80. data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
  81. data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
  82. data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
  83. data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
  84. data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
  85. data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
  86. data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
  87. data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
  88. data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
  89. data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
  90. data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
  91. data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
  92. data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
  93. data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
  94. data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
  95. data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
  96. data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
  97. data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
  98. data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
  99. data/spec/cql/protocol/type_converter_spec.rb +21 -4
  100. data/spec/cql/uuid_spec.rb +10 -3
  101. data/spec/integration/client_spec.rb +251 -28
  102. data/spec/integration/protocol_spec.rb +213 -62
  103. data/spec/integration/regression_spec.rb +4 -1
  104. data/spec/integration/uuid_spec.rb +4 -1
  105. data/spec/support/fake_io_reactor.rb +5 -5
  106. metadata +36 -7
  107. data/lib/cql/client/connection_helper.rb +0 -181
  108. data/spec/cql/client/connection_helper_spec.rb +0 -429
@@ -35,7 +35,11 @@ module Cql
35
35
 
36
36
  describe '#execute' do
37
37
  let :rows_response do
38
- Protocol::RowsResultResponse.new(rows, metadata, nil)
38
+ Protocol::RowsResultResponse.new(rows, metadata, nil, nil)
39
+ end
40
+
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)
39
43
  end
40
44
 
41
45
  let :void_response do
@@ -43,7 +47,7 @@ module Cql
43
47
  end
44
48
 
45
49
  let :prepared_response do
46
- Protocol::PreparedResultResponse.new("\x2a", metadata, nil)
50
+ Protocol::PreparedResultResponse.new("\x2a", metadata, nil, nil)
47
51
  end
48
52
 
49
53
  let :error_response do
@@ -84,12 +88,19 @@ module Cql
84
88
  result.should have(3).items
85
89
  end
86
90
 
91
+ it 'transforms a RawRowsResultResponse to a LazyQueryResult, and mixes in the specified metadata' do
92
+ metadata = [['ks', 'tbl', 'col', :varchar]]
93
+ connection.stub(:send_request).and_return(Future.resolved(raw_rows_response))
94
+ result = runner.execute(connection, request, nil, metadata).value
95
+ result.to_a.should == [{'col' => 'a'}, {'col' => 'b'}]
96
+ end
97
+
87
98
  it 'transforms a VoidResultResponse to a VoidResult' do
88
99
  result = run(void_response)
89
100
  result.should be_a(VoidResult)
90
101
  end
91
102
 
92
- it 'transforms a AuthenticateResponse to an authentication required object' do
103
+ it 'transforms an AuthenticateResponse to an authentication required object' do
93
104
  result = run(authenticate_response)
94
105
  result.should be_a(AuthenticationRequired)
95
106
  result.authentication_class.should == 'TheAuthenticator'
@@ -107,7 +118,7 @@ module Cql
107
118
 
108
119
  it 'sets the #cql field of QueryError when the request is a query request' do
109
120
  begin
110
- run(error_response, Protocol::QueryRequest.new('SELECT * FROM everything', :all))
121
+ run(error_response, Protocol::QueryRequest.new('SELECT * FROM everything', nil, nil, :all))
111
122
  rescue QueryError => e
112
123
  e.cql.should == 'SELECT * FROM everything'
113
124
  else
@@ -147,7 +158,7 @@ module Cql
147
158
  end
148
159
 
149
160
  it 'returns a QueryResult that knows its trace ID' do
150
- connection.stub(:send_request).with(request, anything).and_return(Future.resolved(Protocol::RowsResultResponse.new(rows, metadata, trace_id)))
161
+ connection.stub(:send_request).with(request, anything).and_return(Future.resolved(Protocol::RowsResultResponse.new(rows, metadata, nil, trace_id)))
151
162
  response = runner.execute(connection, request).value
152
163
  response.trace_id.should == trace_id
153
164
  end
@@ -158,6 +169,21 @@ module Cql
158
169
  response.trace_id.should == trace_id
159
170
  end
160
171
  end
172
+
173
+ context 'when the response has a paging state' do
174
+ it 'returns a QueryResult that knows its paging state' do
175
+ connection.stub(:send_request).with(request, anything).and_return(Future.resolved(Protocol::RowsResultResponse.new(rows, metadata, 'foobaz', nil)))
176
+ response = runner.execute(connection, request).value
177
+ response.paging_state.should == 'foobaz'
178
+ end
179
+
180
+ it 'returns a LazyQueryResult that knows its paging state' do
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)))
183
+ response = runner.execute(connection, request, nil, metadata).value
184
+ response.paging_state.should == 'bazbuzz'
185
+ end
186
+ end
161
187
  end
162
188
  end
163
189
  end
@@ -75,13 +75,26 @@ module Cql
75
75
  future.stub(:value).and_return(result)
76
76
  client.execute('SELECT * FROM something', :one).should equal(result)
77
77
  end
78
+
79
+ it 'wraps AsynchronousPagedQueryResult in a synchronous wrapper' do
80
+ cql = 'SELECT * FROM something'
81
+ request = double(:request, cql: cql, values: [])
82
+ async_result = double(:result, paging_state: 'somepagingstate')
83
+ options = {:page_size => 10}
84
+ async_client.stub(:execute).and_return(Future.resolved(AsynchronousQueryPagedQueryResult.new(async_client, request, async_result, options)))
85
+ result1 = client.execute(cql, options)
86
+ result2 = result1.next_page
87
+ async_client.should have_received(:execute).with('SELECT * FROM something', page_size: 10, paging_state: 'somepagingstate')
88
+ result2.should be_a(SynchronousPagedQueryResult)
89
+ end
78
90
  end
79
91
 
80
92
  describe '#prepare' do
81
93
  it 'calls #prepare on the async client, waits for the result and returns a SynchronousFuture' do
82
94
  result = double(:result)
83
95
  metadata = double(:metadata)
84
- async_statement = double(:async_statement, metadata: metadata)
96
+ result_metadata = double(:result_metadata)
97
+ async_statement = double(:async_statement, metadata: metadata, result_metadata: result_metadata)
85
98
  another_future = double(:another_future)
86
99
  async_client.stub(:prepare).with('SELECT * FROM something').and_return(future)
87
100
  future.stub(:value).and_return(async_statement)
@@ -93,6 +106,36 @@ module Cql
93
106
  end
94
107
  end
95
108
 
109
+ describe '#batch' do
110
+ let :batch do
111
+ double(:batch)
112
+ end
113
+
114
+ context 'when called without a block' do
115
+ it 'delegates to the asynchronous client and wraps the returned object in a synchronous wrapper' do
116
+ async_client.stub(:batch).with(:unlogged, trace: true).and_return(batch)
117
+ batch.stub(:execute).and_return(Cql::Future.resolved(VoidResult.new))
118
+ b = client.batch(:unlogged, trace: true)
119
+ b.execute.should be_a(VoidResult)
120
+ end
121
+ end
122
+
123
+ context 'when called with a block' do
124
+ it 'delegates to the asynchronous client' do
125
+ async_client.stub(:batch).with(:counter, trace: true).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
126
+ yielded_batch = nil
127
+ client.batch(:counter, trace: true) { |b| yielded_batch = b }
128
+ yielded_batch.should equal(batch)
129
+ end
130
+
131
+ it 'waits for the operation to complete' do
132
+ async_client.stub(:batch).with(:counter, {}).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
133
+ result = client.batch(:counter) { |b| }
134
+ result.should be_a(VoidResult)
135
+ end
136
+ end
137
+ end
138
+
96
139
  describe '#async' do
97
140
  it 'returns an asynchronous client' do
98
141
  client.async.should equal(async_client)
@@ -11,13 +11,17 @@ module Cql
11
11
  end
12
12
 
13
13
  let :async_statement do
14
- double(:async_statement, metadata: metadata)
14
+ double(:async_statement, metadata: metadata, result_metadata: result_metadata)
15
15
  end
16
16
 
17
17
  let :metadata do
18
18
  double(:metadata)
19
19
  end
20
20
 
21
+ let :result_metadata do
22
+ double(:result_metadata)
23
+ end
24
+
21
25
  let :promise do
22
26
  Promise.new
23
27
  end
@@ -32,6 +36,12 @@ module Cql
32
36
  end
33
37
  end
34
38
 
39
+ describe '#result_metadata' do
40
+ it 'returns the async statement\'s result metadata' do
41
+ statement.result_metadata.should equal(async_statement.result_metadata)
42
+ end
43
+ end
44
+
35
45
  describe '#execute' do
36
46
  it 'it calls #execute on the async statement and waits for the result' do
37
47
  result = double(:result)
@@ -39,6 +49,58 @@ module Cql
39
49
  promise.fulfill(result)
40
50
  statement.execute('one', 'two', :three).should equal(result)
41
51
  end
52
+
53
+ it 'wraps AsynchronousPagedQueryResult in a synchronous wrapper' do
54
+ request = double(:request, values: ['one', 'two'])
55
+ async_result = double(:result, paging_state: 'somepagingstate')
56
+ options = {:page_size => 10}
57
+ async_statement.stub(:execute).and_return(Future.resolved(AsynchronousPreparedPagedQueryResult.new(async_statement, request, async_result, options)))
58
+ result1 = statement.execute('one', 'two', options)
59
+ result2 = result1.next_page
60
+ async_statement.should have_received(:execute).with('one', 'two', page_size: 10, paging_state: 'somepagingstate')
61
+ result2.should be_a(SynchronousPagedQueryResult)
62
+ end
63
+ end
64
+
65
+ describe '#batch' do
66
+ let :batch do
67
+ double(:batch)
68
+ end
69
+
70
+ context 'when called without a block' do
71
+ it 'delegates to the asynchronous statement and wraps the returned object in a synchronous wrapper' do
72
+ async_statement.stub(:batch).with(:unlogged, trace: true).and_return(batch)
73
+ batch.stub(:execute).and_return(Cql::Future.resolved(VoidResult.new))
74
+ b = statement.batch(:unlogged, trace: true)
75
+ b.execute.should be_a(VoidResult)
76
+ end
77
+ end
78
+
79
+ context 'when called with a block' do
80
+ it 'delegates to the asynchronous statement' do
81
+ async_statement.stub(:batch).with(:counter, trace: true).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
82
+ yielded_batch = nil
83
+ statement.batch(:counter, trace: true) { |b| yielded_batch = b }
84
+ yielded_batch.should equal(batch)
85
+ end
86
+
87
+ it 'waits for the operation to complete' do
88
+ async_statement.stub(:batch).with(:counter, anything).and_yield(batch).and_return(Cql::Future.resolved(VoidResult.new))
89
+ result = statement.batch(:counter) { |b| }
90
+ result.should be_a(VoidResult)
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#add_to_batch' do
96
+ it 'delegates to the async statement' do
97
+ batch = double(:batch)
98
+ connection = double(:connection)
99
+ bound_arguments = [1, 2, 3]
100
+ async_statement.stub(:add_to_batch)
101
+ statement.add_to_batch(batch, connection, bound_arguments)
102
+ async_statement.should have_received(:add_to_batch).with(batch, connection, bound_arguments)
103
+ end
42
104
  end
43
105
 
44
106
  describe '#pipeline' do
@@ -393,6 +393,33 @@ module Cql
393
393
  mapped_value.should == 3 * 2
394
394
  end
395
395
 
396
+ it 'will be fulfilled with the specified value' do
397
+ mapped_value = nil
398
+ p = Promise.new
399
+ f = p.future.map(7)
400
+ f.on_value { |v| mapped_value = v }
401
+ p.fulfill(3)
402
+ mapped_value.should == 7
403
+ end
404
+
405
+ it 'will be fulfilled with the result of the given block, even if a value is specified' do
406
+ mapped_value = nil
407
+ p = Promise.new
408
+ f = p.future.map(7) { |v| v * 2 }
409
+ f.on_value { |v| mapped_value = v }
410
+ p.fulfill(3)
411
+ mapped_value.should == 3 * 2
412
+ end
413
+
414
+ it 'will be fulfilled with nil when neither value nor block is specified' do
415
+ mapped_value = 3
416
+ p = Promise.new
417
+ f = p.future.map
418
+ f.on_value { |v| mapped_value = v }
419
+ p.fulfill(3)
420
+ mapped_value.should be_nil
421
+ end
422
+
396
423
  it 'fails when the original future fails' do
397
424
  failed = false
398
425
  p = Promise.new
@@ -432,13 +459,34 @@ module Cql
432
459
 
433
460
  describe '#recover' do
434
461
  context 'returns a new future that' do
435
- it 'becomes fulfilled with a value when the source future fails' do
462
+ it 'becomes fulfilled with a value created by the block when the source future fails' do
436
463
  p = Promise.new
437
464
  f = p.future.recover { 'foo' }
438
465
  p.fail(error)
439
466
  f.value.should == 'foo'
440
467
  end
441
468
 
469
+ it 'becomes fulfilled with a specfied value when the source future fails' do
470
+ p = Promise.new
471
+ f = p.future.recover('bar')
472
+ p.fail(error)
473
+ f.value.should == 'bar'
474
+ end
475
+
476
+ it 'becomes fulfilled with a value created by the block even when a value is specified when the source future fails' do
477
+ p = Promise.new
478
+ f = p.future.recover('bar') { 'foo' }
479
+ p.fail(error)
480
+ f.value.should == 'foo'
481
+ end
482
+
483
+ it 'becomes fulfilled with nil value when no value nor block is specified and the source future fails' do
484
+ p = Promise.new
485
+ f = p.future.recover
486
+ p.fail(error)
487
+ f.value.should be_nil
488
+ end
489
+
442
490
  it 'yields the error to the block' do
443
491
  p = Promise.new
444
492
  f = p.future.recover { |e| e.message }
@@ -644,7 +692,7 @@ module Cql
644
692
  described_class.resolved('hello world')
645
693
  end
646
694
 
647
- it 'returns a future which is resolved' do
695
+ it 'is resolved' do
648
696
  future.should be_resolved
649
697
  end
650
698
 
@@ -7,7 +7,7 @@ module Cql
7
7
  module Protocol
8
8
  describe CqlProtocolHandler do
9
9
  let :protocol_handler do
10
- described_class.new(connection, scheduler)
10
+ described_class.new(connection, scheduler, 1)
11
11
  end
12
12
 
13
13
  let :connection do
@@ -114,7 +114,7 @@ module Cql
114
114
 
115
115
  context 'when a compressor is specified' do
116
116
  let :protocol_handler do
117
- described_class.new(connection, scheduler, compressor)
117
+ described_class.new(connection, scheduler, 1, compressor)
118
118
  end
119
119
 
120
120
  let :compressor do
@@ -147,8 +147,19 @@ module Cql
147
147
  f2 = protocol_handler.send_request(request)
148
148
  connection.data_listener.call("\x81\x01\x00\x08\x00\x00\x00\x12FAKECOMPRESSEDBODY")
149
149
  connection.data_listener.call("\x81\x01\x01\x08\x00\x00\x00\x12FAKECOMPRESSEDBODY")
150
- f1.value.should == Protocol::PreparedResultResponse.new(id, [["cql_rb_911", "users", "user_name", :varchar]], nil)
151
- f2.value.should == Protocol::PreparedResultResponse.new(id, [["cql_rb_911", "users", "user_name", :varchar]], nil)
150
+ f1.value.should == Protocol::PreparedResultResponse.new(id, [["cql_rb_911", "users", "user_name", :varchar]], nil, nil)
151
+ f2.value.should == Protocol::PreparedResultResponse.new(id, [["cql_rb_911", "users", "user_name", :varchar]], nil, nil)
152
+ end
153
+ end
154
+
155
+ context 'when a protocol version is specified' do
156
+ let :protocol_handler do
157
+ described_class.new(connection, scheduler, 7)
158
+ end
159
+
160
+ it 'sets the protocol version in the header' do
161
+ protocol_handler.send_request(request)
162
+ buffer.to_s[0].should == "\x07"
152
163
  end
153
164
  end
154
165
 
@@ -266,7 +277,7 @@ module Cql
266
277
  end
267
278
 
268
279
  it 'registers the keyspace it has changed to' do
269
- f = protocol_handler.send_request(Protocol::QueryRequest.new('USE hello', :one))
280
+ f = protocol_handler.send_request(Protocol::QueryRequest.new('USE hello', nil, nil, :one))
270
281
  connection.data_listener.call([0x81, 0, 0, 8, 4 + 2 + 5, 3, 5].pack('C4N2n') + 'hello')
271
282
  f.value
272
283
  protocol_handler.keyspace.should == 'hello'
@@ -100,8 +100,8 @@ module Cql
100
100
  end
101
101
 
102
102
  it 'decodes a negative long' do
103
- buffer = ByteBuffer.new("\xff\xff\xff\xff\xff\xff\xff\xff")
104
- Decoding.read_long!(buffer).should == -1
103
+ buffer = ByteBuffer.new("\xff\xee\xdd\xcc\xbb\xaa\x99\x88")
104
+ Decoding.read_long!(buffer).should == 0xffeeddccbbaa9988 - 0x10000000000000000
105
105
  end
106
106
 
107
107
  it 'consumes the bytes' do
@@ -162,8 +162,8 @@ module Cql
162
162
  end
163
163
 
164
164
  it 'decodes a negative int' do
165
- buffer = ByteBuffer.new("\xff\xff\xff\xff")
166
- Decoding.read_int!(buffer).should == -1
165
+ buffer = ByteBuffer.new("\xff\xee\xdd\xcc")
166
+ Decoding.read_int!(buffer).should == 0xffeeddcc - 0x100000000
167
167
  end
168
168
 
169
169
  it 'consumes the bytes' do
@@ -470,14 +470,24 @@ module Cql
470
470
  Decoding.read_consistency!(buffer).should == :each_quorum
471
471
  end
472
472
 
473
+ it 'decodes SERIAL' do
474
+ buffer = ByteBuffer.new("\x00\x08")
475
+ Decoding.read_consistency!(buffer).should == :serial
476
+ end
477
+
478
+ it 'decodes LOCAL_SERIAL' do
479
+ buffer = ByteBuffer.new("\x00\x09")
480
+ Decoding.read_consistency!(buffer).should == :local_serial
481
+ end
482
+
473
483
  it 'decodes LOCAL_ONE' do
474
- buffer = ByteBuffer.new("\x00\x10")
484
+ buffer = ByteBuffer.new("\x00\x0a")
475
485
  Decoding.read_consistency!(buffer).should == :local_one
476
486
  end
477
487
 
478
488
  it 'raises an exception for an unknown consistency' do
479
489
  expect { Decoding.read_consistency!(ByteBuffer.new("\xff\xff")) }.to raise_error(DecodingError)
480
- expect { Decoding.read_consistency!(ByteBuffer.new("\x00\x0a")) }.to raise_error(DecodingError)
490
+ expect { Decoding.read_consistency!(ByteBuffer.new("\x00\x0f")) }.to raise_error(DecodingError)
481
491
  end
482
492
  end
483
493
 
@@ -238,7 +238,9 @@ module Cql
238
238
  :all => "\x00\x05",
239
239
  :local_quorum => "\x00\x06",
240
240
  :each_quorum => "\x00\x07",
241
- :local_one => "\x00\x10",
241
+ :serial => "\x00\x08",
242
+ :local_serial => "\x00\x09",
243
+ :local_one => "\x00\x0a",
242
244
  }.each do |consistency, expected_encoding|
243
245
  it "encodes #{consistency}" do
244
246
  Encoding.write_consistency(buffer, consistency)
@@ -6,86 +6,135 @@ require 'spec_helper'
6
6
  module Cql
7
7
  module Protocol
8
8
  describe FrameEncoder do
9
- let :encoder do
10
- described_class.new
11
- end
12
-
13
9
  describe '#encode_frame' do
14
- it 'returns a rendered request frame for the specified channel' do
15
- request = PrepareRequest.new('SELECT * FROM things')
16
- stream_id = 3
17
- encoded_frame = encoder.encode_frame(request, stream_id)
18
- encoded_frame.to_s.should == "\x01\x00\x03\x09\x00\x00\x00\x18\x00\x00\x00\x14SELECT * FROM things"
10
+ let :request do
11
+ double(:request)
12
+ end
13
+
14
+ let :compressor do
15
+ double(:compressor)
19
16
  end
20
17
 
21
- it 'appends a rendered request frame to the specified buffer' do
18
+ before do
19
+ request.stub(:opcode).and_return(0x77)
20
+ request.stub(:trace?).and_return(false)
21
+ request.stub(:write) { |pv, bb| bb }
22
+ end
23
+
24
+ it 'asks the request to write itself to a buffer' do
25
+ encoder = described_class.new(1)
26
+ encoder.encode_frame(request)
27
+ request.should have_received(:write).with(anything, an_instance_of(ByteBuffer))
28
+ end
29
+
30
+ it 'appends what the request wrote to the specified buffer, after a header' do
22
31
  buffer = ByteBuffer.new('hello')
23
- request = PrepareRequest.new('SELECT * FROM things')
24
- stream_id = 3
25
- encoded_frame = encoder.encode_frame(request, stream_id, buffer)
26
- buffer.to_s.should == "hello\x01\x00\x03\x09\x00\x00\x00\x18\x00\x00\x00\x14SELECT * FROM things"
32
+ request.stub(:write) { |pv, bb| bb << "\x01\x02\x03" }
33
+ encoder = described_class.new(1)
34
+ encoder.encode_frame(request, 0, buffer)
35
+ buffer.to_s[ 0, 5].should == 'hello'
36
+ buffer.to_s[13, 3].should == "\x01\x02\x03"
27
37
  end
28
38
 
29
39
  it 'returns the specified buffer' do
30
40
  buffer = ByteBuffer.new('hello')
31
- request = PrepareRequest.new('SELECT * FROM things')
32
- stream_id = 3
33
- encoded_frame = encoder.encode_frame(request, stream_id, buffer)
34
- encoded_frame.should equal(buffer)
41
+ encoder = described_class.new(1)
42
+ returned_buffer = encoder.encode_frame(request, 0, buffer)
43
+ returned_buffer.should equal(buffer)
35
44
  end
36
45
 
37
- context 'with a compressor' do
38
- let :encoder do
39
- described_class.new(compressor)
40
- end
46
+ it 'passes the protocol version to the request' do
47
+ encoder = described_class.new(7)
48
+ encoder.encode_frame(request)
49
+ request.should have_received(:write).with(7, anything)
50
+ end
41
51
 
42
- let :compressor do
43
- double(:compressor)
44
- end
52
+ it 'encodes a header with the specified protocol version' do
53
+ encoder = described_class.new(7)
54
+ buffer = encoder.encode_frame(request)
55
+ buffer.to_s[0].should == "\x07"
56
+ end
45
57
 
46
- let :request do
47
- PrepareRequest.new('SELECT * FROM things')
48
- end
58
+ it 'encodes a header with the tracing flag set' do
59
+ request.stub(:trace?).and_return(true)
60
+ encoder = described_class.new(1)
61
+ buffer = encoder.encode_frame(request)
62
+ buffer.to_s[1].should == "\x02"
63
+ end
49
64
 
50
- let :compressed_frame do
51
- encoder.encode_frame(request, 3).to_s
52
- end
65
+ it 'encodes a header with the specified stream ID' do
66
+ encoder = described_class.new(1)
67
+ buffer = encoder.encode_frame(request, 9)
68
+ buffer.to_s[2].should == "\x09"
69
+ end
70
+
71
+ it 'complains when the stream ID is less than 0 or more than 127' do
72
+ encoder = described_class.new(1)
73
+ expect { encoder.encode_frame(request, -1) }.to raise_error(InvalidStreamIdError)
74
+ expect { encoder.encode_frame(request, 128) }.to raise_error(InvalidStreamIdError)
75
+ end
76
+
77
+ it 'encodes a header with the right opcode' do
78
+ encoder = described_class.new(1)
79
+ buffer = encoder.encode_frame(request, 9)
80
+ buffer.to_s[3].should == "\x77"
81
+ end
53
82
 
83
+ it 'encodes the body size' do
84
+ request.stub(:write).and_return(ByteBuffer.new('helloworld'))
85
+ encoder = described_class.new(1)
86
+ buffer = encoder.encode_frame(request, 9)
87
+ buffer.to_s[4, 4].should == "\x00\x00\x00\x0a"
88
+ end
89
+
90
+ context 'when a compressor has been specified' do
54
91
  before do
55
- compressor.stub(:compress?).with("\x00\x00\x00\x14SELECT * FROM things").and_return(true)
56
- compressor.stub(:compress).with("\x00\x00\x00\x14SELECT * FROM things").and_return('FAKECOMPRESSEDBODY')
92
+ request.stub(:compressable?).and_return(true)
93
+ request.stub(:write).and_return(ByteBuffer.new('helloworld'))
94
+ compressor.stub(:compress?).and_return(true)
95
+ compressor.stub(:compress).with('helloworld').and_return('COMPRESSEDFRAME')
57
96
  end
58
97
 
59
- it 'sets the compression flag' do
60
- compressed_frame[1].should == "\x01"
98
+ it 'compresses the request' do
99
+ encoder = described_class.new(1, compressor)
100
+ buffer = encoder.encode_frame(request)
101
+ buffer.to_s[8, 100].should == 'COMPRESSEDFRAME'
61
102
  end
62
103
 
63
- it 'sets the size to the compressed size' do
64
- compressed_frame[4, 4].unpack('N').first.should == 18
104
+ it 'sets the compression flag' do
105
+ encoder = described_class.new(1, compressor)
106
+ buffer = encoder.encode_frame(request)
107
+ buffer.to_s[1, 1].should == "\x01"
65
108
  end
66
109
 
67
- it 'compresses the frame body' do
68
- compressed_frame[8, 18].should == 'FAKECOMPRESSEDBODY'
69
- compressed_frame.bytesize.should == 26
110
+ it 'sets both the compression flag and the tracing flag' do
111
+ request.stub(:trace?).and_return(true)
112
+ encoder = described_class.new(1, compressor)
113
+ buffer = encoder.encode_frame(request)
114
+ buffer.to_s[1, 1].should == "\x03"
70
115
  end
71
116
 
72
- it 'does not clobber the trace flag' do
73
- request = PrepareRequest.new('SELECT * FROM things', true)
74
- stream_id = 3
75
- compressed_frame = encoder.encode_frame(request, stream_id)
76
- compressed_frame.to_s[1].should == "\x03"
117
+ it 'encodes the compressed body size' do
118
+ encoder = described_class.new(1, compressor)
119
+ buffer = encoder.encode_frame(request)
120
+ buffer.to_s[4, 4].should == "\x00\x00\x00\x0f"
77
121
  end
78
122
 
79
- it 'does not compress when the compressor responds to #compress? with false' do
80
- compressor.stub(:compress?).and_return(false)
81
- compressed_frame[1].should == "\x00"
82
- compressed_frame.should include('SELECT * FROM things')
83
- compressed_frame.bytesize.should == 32
123
+ it 'does not compress uncompressable frames' do
124
+ request.stub(:compressable?).and_return(false)
125
+ encoder = described_class.new(1, compressor)
126
+ buffer = encoder.encode_frame(request)
127
+ compressor.should_not have_received(:compress?)
128
+ compressor.should_not have_received(:compress)
84
129
  end
85
130
  end
86
131
  end
87
132
 
88
133
  describe '#change_stream_id' do
134
+ let :encoder do
135
+ described_class.new
136
+ end
137
+
89
138
  it 'changes the stream ID byte' do
90
139
  buffer = ByteBuffer.new("\x01\x00\x03\x02\x00\x00\x00\x00")
91
140
  encoder.change_stream_id(99, buffer)