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
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Client
8
+ describe ExecuteOptionsDecoder do
9
+ let :decoder do
10
+ described_class.new(:two)
11
+ end
12
+
13
+ describe '#decode_options' do
14
+ it 'returns the default consistency' do
15
+ options = decoder.decode_options({})
16
+ options.should include(consistency: :two)
17
+ end
18
+
19
+ it 'returns the default consistency when given no options' do
20
+ options = decoder.decode_options
21
+ options.should include(consistency: :two)
22
+ end
23
+
24
+ it 'returns the default consistency when given nil' do
25
+ options = decoder.decode_options(nil)
26
+ options.should include(consistency: :two)
27
+ end
28
+
29
+ it 'uses the consistency given in the options' do
30
+ options = decoder.decode_options(consistency: :three)
31
+ options.should include(consistency: :three)
32
+ end
33
+
34
+ it 'uses the consistency given as a symbol' do
35
+ options = decoder.decode_options(:three)
36
+ options.should include(consistency: :three)
37
+ end
38
+
39
+ it 'defaults to no serial consistency' do
40
+ options = decoder.decode_options({})
41
+ options.should_not have_key(:serial_consistency)
42
+ end
43
+
44
+ it 'uses the serial consistency given in the options' do
45
+ options = decoder.decode_options(serial_consistency: :local_serial)
46
+ options.should include(serial_consistency: :local_serial)
47
+ end
48
+
49
+ it 'defaults to no tracing' do
50
+ options = decoder.decode_options({})
51
+ options.should_not have_key(:trace)
52
+ end
53
+
54
+ it 'uses the tracing value given in the options' do
55
+ options = decoder.decode_options(trace: true)
56
+ options.should include(trace: true)
57
+ options = decoder.decode_options(trace: false)
58
+ options.should include(trace: false)
59
+ end
60
+
61
+ it 'defaults to no timeout' do
62
+ options = decoder.decode_options({})
63
+ options.should_not have_key(:timeout)
64
+ end
65
+
66
+ it 'uses the timeout value given in the options' do
67
+ options = decoder.decode_options(timeout: 3)
68
+ options.should include(timeout: 3)
69
+ end
70
+
71
+ context 'when called with multiple options' do
72
+ it 'merges the options' do
73
+ options = decoder.decode_options({:tracing => true, :timeout => 3}, {:consistency => :quorum, :timeout => 4})
74
+ options.should eql(tracing: true, timeout: 4, consistency: :quorum)
75
+ end
76
+
77
+ it 'uses the default consistency' do
78
+ options = decoder.decode_options({tracing: true, timeout: 3}, {:timeout => 4})
79
+ options.should eql(tracing: true, timeout: 4, consistency: :two)
80
+ end
81
+
82
+ it 'accepts nil' do
83
+ options = decoder.decode_options(nil, {tracing: true, timeout: 3}, nil, {:timeout => 4})
84
+ options.should eql(tracing: true, timeout: 4, consistency: :two)
85
+ end
86
+
87
+ it 'accepts consistencies given as symbols' do
88
+ options = decoder.decode_options({tracing: true, timeout: 3}, :quorum)
89
+ options.should eql(tracing: true, timeout: 3, consistency: :quorum)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -16,31 +16,31 @@ module Cql
16
16
 
17
17
  describe '#use_keyspace' do
18
18
  it 'sends a query request with a USE statement' do
19
- connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one), nil).and_return(Future.resolved)
20
- f = keyspace_changer.use_keyspace('important_stuff', connection)
19
+ connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', nil, nil, :one), nil).and_return(Future.resolved)
20
+ f = keyspace_changer.use_keyspace(connection, 'important_stuff')
21
21
  connection.should have_received(:send_request)
22
22
  end
23
23
 
24
24
  it 'accepts quoted keyspace names' do
25
- connection.stub(:send_request).with(Protocol::QueryRequest.new('USE "ImportantStuff"', :one), nil).and_return(Future.resolved)
26
- f = keyspace_changer.use_keyspace('"ImportantStuff"', connection)
25
+ connection.stub(:send_request).with(Protocol::QueryRequest.new('USE "ImportantStuff"', nil, nil, :one), nil).and_return(Future.resolved)
26
+ f = keyspace_changer.use_keyspace(connection, '"ImportantStuff"')
27
27
  connection.should have_received(:send_request)
28
28
  end
29
29
 
30
30
  context 'returns a future that' do
31
31
  it 'immediately resolves to the given connection when the keyspace is nil' do
32
- f = keyspace_changer.use_keyspace(nil, connection)
32
+ f = keyspace_changer.use_keyspace(connection, nil)
33
33
  f.value.should equal(connection)
34
34
  end
35
35
 
36
36
  it 'fails with an InvalidKeyspaceNameError when the keyspace name is invalid' do
37
- f = keyspace_changer.use_keyspace('TRUNCATE important_stuff', connection)
37
+ f = keyspace_changer.use_keyspace(connection, 'TRUNCATE important_stuff')
38
38
  expect { f.value }.to raise_error(InvalidKeyspaceNameError)
39
39
  end
40
40
 
41
41
  it 'resolves to the given connection' do
42
- connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', :one), nil).and_return(Future.resolved)
43
- f = keyspace_changer.use_keyspace('important_stuff', connection)
42
+ connection.stub(:send_request).with(Protocol::QueryRequest.new('USE important_stuff', nil, nil, :one), nil).and_return(Future.resolved)
43
+ f = keyspace_changer.use_keyspace(connection, 'important_stuff')
44
44
  f.value.should equal(connection)
45
45
  end
46
46
  end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Client
8
+ describe PeerDiscovery do
9
+ let :peer_discovery do
10
+ described_class.new(seed_connections)
11
+ end
12
+
13
+ describe '#new_hosts' do
14
+ let :seed_connections do
15
+ [
16
+ FakeConnection.new('host0', 9042, 5, {:data_center => 'dc0', :host_id => Uuid.new('00000000-0000-0000-0000-000000000000')}),
17
+ FakeConnection.new('host1', 9042, 5, {:data_center => 'dc0', :host_id => Uuid.new('11111111-1111-1111-1111-111111111111')}),
18
+ FakeConnection.new('host2', 9042, 5, {:data_center => 'dc0', :host_id => Uuid.new('22222222-2222-2222-2222-222222222222')}),
19
+ ]
20
+ end
21
+
22
+ let :peer_metadata do
23
+ [
24
+ ['system', 'peers', 'peer', :inet],
25
+ ['system', 'peers', 'data_center', :varchar],
26
+ ['system', 'peers', 'host_id', :uuid],
27
+ ['system', 'peers', 'rpc_address', :inet],
28
+ ]
29
+ end
30
+
31
+ let :peer_rows do
32
+ [
33
+ {'peer' => IPAddr.new('2.0.0.0'), 'rpc_address' => IPAddr.new('1.0.0.0'), 'data_center' => 'dc0', 'host_id' => Uuid.new('00000000-0000-0000-0000-000000000000')},
34
+ {'peer' => IPAddr.new('2.0.0.1'), 'rpc_address' => IPAddr.new('1.0.0.1'), 'data_center' => 'dc0', 'host_id' => Uuid.new('11111111-1111-1111-1111-111111111111')},
35
+ {'peer' => IPAddr.new('2.0.0.2'), 'rpc_address' => IPAddr.new('1.0.0.2'), 'data_center' => 'dc0', 'host_id' => Uuid.new('22222222-2222-2222-2222-222222222222')},
36
+ {'peer' => IPAddr.new('2.0.0.3'), 'rpc_address' => IPAddr.new('1.0.0.3'), 'data_center' => 'dc0', 'host_id' => Uuid.new('33333333-3333-3333-3333-333333333333')},
37
+ {'peer' => IPAddr.new('2.0.0.4'), 'rpc_address' => IPAddr.new('1.0.0.4'), 'data_center' => 'dc0', 'host_id' => Uuid.new('44444444-4444-4444-4444-444444444444')},
38
+ ]
39
+ end
40
+
41
+ let :peer_rows_response do
42
+ Protocol::RowsResultResponse.new(peer_rows, peer_metadata, nil, nil)
43
+ end
44
+
45
+ before do
46
+ seed_connections.each do |connection|
47
+ connection.handle_request do |request|
48
+ peer_rows_response
49
+ end
50
+ end
51
+ end
52
+
53
+ it 'selects all rows from the "peers" system table' do
54
+ peer_discovery.new_hosts.value
55
+ selected_connection = seed_connections.find { |c| c.requests.any? }
56
+ request = selected_connection.requests.first
57
+ columns, table = request.cql.scan(/SELECT (.*) FROM (\S+)/).flatten
58
+ table.should == 'system.peers'
59
+ columns.should include('peer')
60
+ columns.should include('data_center')
61
+ columns.should include('host_id')
62
+ columns.should include('rpc_address')
63
+ end
64
+
65
+ it 'returns a future that resolves to the addresses of all peers that were not already in the set of seed nodes' do
66
+ new_hosts = peer_discovery.new_hosts
67
+ new_hosts.value.should == %w[1.0.0.3 1.0.0.4]
68
+ end
69
+
70
+ it 'returns an empty list of addresses when there are no peers that are not in the set of seed nodes' do
71
+ peer_rows.pop(2)
72
+ new_hosts = peer_discovery.new_hosts
73
+ new_hosts.value.should be_empty
74
+ end
75
+
76
+ it 'returns the value of the "peer" column when the "rpc_address" column contains "0.0.0.0"' do
77
+ peer_rows.each do |row|
78
+ row['rpc_address'] = IPAddr.new('0.0.0.0')
79
+ end
80
+ new_hosts = peer_discovery.new_hosts
81
+ new_hosts.value.should == %w[2.0.0.3 2.0.0.4]
82
+ end
83
+
84
+ it 'only returns addresses to nodes that are in the same data centers as the seed nodes' do
85
+ peer_rows[3]['data_center'] = 'dc1'
86
+ new_hosts = peer_discovery.new_hosts
87
+ new_hosts.value.should == %w[1.0.0.4]
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,352 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Client
8
+ shared_context 'query_result_setup' do
9
+ let :metadata do
10
+ [
11
+ ['ks', 'tbl', 'col1', :varchar],
12
+ ['ks', 'tbl', 'col2', :double],
13
+ ]
14
+ end
15
+
16
+ let :trace_id do
17
+ double(:trace_id)
18
+ end
19
+
20
+ let :rows do
21
+ [double(:row1), double(:row2), double(:row3)]
22
+ end
23
+ end
24
+
25
+ shared_examples 'query_result_shared' do
26
+ describe '#metadata' do
27
+ it 'wraps the raw metadata in a ResultMetadata' do
28
+ result.metadata['col1'].should == ColumnMetadata.new('ks', 'tbl', 'col1', :varchar)
29
+ end
30
+ end
31
+
32
+ describe '#each' do
33
+ it 'yields each row' do
34
+ yielded_rows = []
35
+ result.each { |r| yielded_rows << r }
36
+ yielded_rows.should == rows
37
+ end
38
+
39
+ it 'can be iterated multiple times' do
40
+ yielded_rows = []
41
+ result.each { |r| yielded_rows << r }
42
+ result.each { |r| yielded_rows << r }
43
+ yielded_rows.should == rows + rows
44
+ end
45
+
46
+ it 'is aliased as #each_row' do
47
+ result.each_row { }
48
+ end
49
+
50
+ it 'returns an Enumerable when no block is given' do
51
+ result.each.to_a.should == rows
52
+ end
53
+ end
54
+
55
+ context 'when used as an Enumerable' do
56
+ before do
57
+ rows.each_with_index { |r, i| r.stub(:[]).with('col2').and_return(i.to_f) }
58
+ end
59
+
60
+ it 'transforms the rows' do
61
+ result.map { |r| r['col2'] * 2 }.should == [0.0, 2.0, 4.0]
62
+ end
63
+
64
+ it 'filters the rows' do
65
+ result.select { |r| r['col2'] > 0 }.should == rows.drop(1)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe QueryResult do
71
+ let :result do
72
+ described_class.new(metadata, rows, trace_id, nil)
73
+ end
74
+
75
+ include_context 'query_result_setup'
76
+ include_examples 'query_result_shared'
77
+
78
+ describe '#empty?' do
79
+ it 'returns true when there are no rows' do
80
+ described_class.new(metadata, [], nil, nil).should be_empty
81
+ end
82
+
83
+ it 'returns false when there are rows' do
84
+ result.should_not be_empty
85
+ end
86
+ end
87
+ end
88
+
89
+ describe LazyQueryResult do
90
+ let :result do
91
+ described_class.new(metadata, lazy_rows, trace_id, nil)
92
+ end
93
+
94
+ let :lazy_rows do
95
+ double(:lazy_rows)
96
+ end
97
+
98
+ include_context 'query_result_setup'
99
+
100
+ before do
101
+ lazy_rows.stub(:rows).and_return(nil)
102
+ lazy_rows.stub(:materialize) do |md|
103
+ raise '#materialize called twice' if lazy_rows.rows
104
+ md.should == metadata
105
+ lazy_rows.stub(:rows).and_return(rows)
106
+ rows
107
+ end
108
+ end
109
+
110
+ include_examples 'query_result_shared'
111
+
112
+ describe '#empty?' do
113
+ it 'returns true when there are no rows' do
114
+ lazy_rows.stub(:materialize) do
115
+ lazy_rows.stub(:rows).and_return([])
116
+ []
117
+ end
118
+ result.should be_empty
119
+ end
120
+
121
+ it 'returns false when there are rows' do
122
+ result.should_not be_empty
123
+ end
124
+ end
125
+ end
126
+
127
+ shared_examples 'paged_query_result' do
128
+ let :metadata do
129
+ ResultMetadata.new([['ks', 'tbl', 'col1', :varchar], ['ks', 'tbl', 'col2', :double]])
130
+ end
131
+
132
+ describe '#metadata' do
133
+ it 'delegates to the wrapped result' do
134
+ query_result.stub(:metadata).and_return(metadata)
135
+ paged_query_result.metadata.should == metadata
136
+ end
137
+ end
138
+
139
+ describe '#trace_id' do
140
+ it 'delegates to the wrapped result' do
141
+ query_result.stub(:trace_id).and_return('foobaz')
142
+ paged_query_result.trace_id.should == 'foobaz'
143
+ end
144
+ end
145
+
146
+ describe '#paging_state' do
147
+ it 'delegates to the wrapped result' do
148
+ query_result.stub(:paging_state).and_return('foobaz')
149
+ paged_query_result.paging_state.should == 'foobaz'
150
+ end
151
+ end
152
+
153
+ describe '#empty?' do
154
+ it 'delegates to the wrapped result' do
155
+ query_result.stub(:empty?).and_return(true)
156
+ paged_query_result.should be_empty
157
+ end
158
+ end
159
+
160
+ describe '#each' do
161
+ it 'delegates to the wrapped result' do
162
+ query_result.stub(:each).and_yield(:row1).and_yield(:row2)
163
+ rows = paged_query_result.each_with_object([]) { |row, rows| rows << row }
164
+ rows.should == [:row1, :row2]
165
+ end
166
+ end
167
+ end
168
+
169
+ shared_examples 'asynchronous_paged_query_result' do
170
+ include_examples 'paged_query_result'
171
+
172
+ describe '#last_page?' do
173
+ it 'returns true when the result has no paging state' do
174
+ query_result.stub(:paging_state).and_return(nil)
175
+ paged_query_result.should be_last_page
176
+ end
177
+
178
+ it 'returns false when the result has a paging state' do
179
+ paged_query_result.should_not be_last_page
180
+ end
181
+ end
182
+ end
183
+
184
+ describe AsynchronousQueryPagedQueryResult do
185
+ let :paged_query_result do
186
+ described_class.new(client, request, query_result, options)
187
+ end
188
+
189
+ let :client do
190
+ double(:client)
191
+ end
192
+
193
+ let :request do
194
+ double(:request, cql: 'SELECT * FROM something WHERE id = ?', values: ['foo'])
195
+ end
196
+
197
+ let :query_result do
198
+ double(:query_result, paging_state: 'thepagingstate')
199
+ end
200
+
201
+ let :options do
202
+ {:trace => true, :timeout => 3}
203
+ end
204
+
205
+ include_examples 'asynchronous_paged_query_result'
206
+
207
+ describe '#next_page' do
208
+ let :next_query_result do
209
+ double(:next_query_result, paging_state: 'thenextpagingstate')
210
+ end
211
+
212
+ before do
213
+ client.stub(:execute).and_return(Future.resolved(next_query_result))
214
+ end
215
+
216
+ it 'calls the client and passes the paging state' do
217
+ paged_query_result.next_page.value
218
+ client.should have_received(:execute).with(anything, anything, hash_including(paging_state: 'thepagingstate'))
219
+ end
220
+
221
+ it 'calls the client and passes the options' do
222
+ paged_query_result.next_page.value
223
+ client.should have_received(:execute).with(anything, anything, hash_including(options))
224
+ end
225
+
226
+ it 'calls the client and passes the CQL' do
227
+ paged_query_result.next_page.value
228
+ client.should have_received(:execute).with(request.cql, anything, anything)
229
+ end
230
+
231
+ it 'calls the client and passes the bound values' do
232
+ paged_query_result.next_page.value
233
+ client.should have_received(:execute).with(anything, 'foo', anything)
234
+ end
235
+
236
+ it 'handles the case when there are multiple bound values' do
237
+ request.stub(:values).and_return(['foo', 3, 'bar', 4])
238
+ paged_query_result.next_page.value
239
+ client.should have_received(:execute).with(anything, 'foo', 3, 'bar', 4, anything)
240
+ end
241
+
242
+ it 'handles the case when there are no bound values' do
243
+ request.stub(:values).and_return(nil)
244
+ paged_query_result.next_page.value
245
+ client.should have_received(:execute).with(request.cql, an_instance_of(Hash))
246
+ end
247
+
248
+ it 'returns the result of the call' do
249
+ f = paged_query_result.next_page
250
+ f.value.should equal(next_query_result)
251
+ end
252
+ end
253
+ end
254
+
255
+ describe AsynchronousPreparedPagedQueryResult do
256
+ let :paged_query_result do
257
+ described_class.new(statement, request, query_result, options)
258
+ end
259
+
260
+ let :statement do
261
+ double(:statement)
262
+ end
263
+
264
+ let :request do
265
+ double(:request, values: ['foo', 3])
266
+ end
267
+
268
+ let :query_result do
269
+ double(:query_result, paging_state: 'thepagingstate')
270
+ end
271
+
272
+ let :options do
273
+ {:trace => true, :timeout => 3}
274
+ end
275
+
276
+ include_examples 'asynchronous_paged_query_result'
277
+
278
+ describe '#next_page' do
279
+ let :next_query_result do
280
+ double(:next_query_result, paging_state: 'thenextpagingstate')
281
+ end
282
+
283
+ before do
284
+ statement.stub(:execute).and_return(Future.resolved(next_query_result))
285
+ end
286
+
287
+ it 'calls the statement and passes the paging state' do
288
+ paged_query_result.next_page.value
289
+ statement.should have_received(:execute).with(anything, anything, hash_including(paging_state: 'thepagingstate'))
290
+ end
291
+
292
+ it 'calls the statement and passes the options' do
293
+ paged_query_result.next_page.value
294
+ statement.should have_received(:execute).with(anything, anything, hash_including(options))
295
+ end
296
+
297
+ it 'calls the statement and passes the bound values' do
298
+ paged_query_result.next_page.value
299
+ statement.should have_received(:execute).with('foo', 3, anything)
300
+ end
301
+
302
+ it 'handles the case when there are no bound values' do
303
+ request.stub(:values).and_return(nil)
304
+ paged_query_result.next_page.value
305
+ statement.should have_received(:execute).with(an_instance_of(Hash))
306
+ end
307
+
308
+ it 'returns the result of the call' do
309
+ f = paged_query_result.next_page
310
+ f.value.should equal(next_query_result)
311
+ end
312
+ end
313
+ end
314
+
315
+ describe SynchronousPagedQueryResult do
316
+ let :paged_query_result do
317
+ described_class.new(asynchronous_paged_query_result)
318
+ end
319
+
320
+ let :asynchronous_paged_query_result do
321
+ double(:asynchronous_paged_query_result)
322
+ end
323
+
324
+ describe '#next_page' do
325
+ let :next_query_result do
326
+ double(:next_query_result)
327
+ end
328
+
329
+ it 'delegates to the wrapped query result and wraps the result in an instance of itself' do
330
+ next_query_result.stub(:next_page).and_return(Future.resolved(next_query_result))
331
+ asynchronous_paged_query_result.stub(:next_page).and_return(Future.resolved(next_query_result))
332
+ second_page = paged_query_result.next_page
333
+ second_page.next_page
334
+ next_query_result.should have_received(:next_page)
335
+ end
336
+ end
337
+
338
+ describe '#last_page?' do
339
+ it 'delegates to the wrapped query result' do
340
+ asynchronous_paged_query_result.stub(:last_page?).and_return(true)
341
+ paged_query_result.should be_last_page
342
+ end
343
+ end
344
+
345
+ describe '#async' do
346
+ it 'returns the asynchronous results' do
347
+ paged_query_result.async.should equal(asynchronous_paged_query_result)
348
+ end
349
+ end
350
+ end
351
+ end
352
+ end