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