cql-rb 1.2.2 → 2.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
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)