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.
- checksums.yaml +4 -4
- data/.yardopts +4 -0
- data/README.md +139 -17
- data/lib/cql/client.rb +237 -8
- data/lib/cql/client/asynchronous_client.rb +138 -54
- data/lib/cql/client/asynchronous_prepared_statement.rb +41 -6
- data/lib/cql/client/authenticators.rb +46 -0
- data/lib/cql/client/batch.rb +115 -0
- data/lib/cql/client/connector.rb +255 -0
- data/lib/cql/client/execute_options_decoder.rb +25 -9
- data/lib/cql/client/keyspace_changer.rb +5 -5
- data/lib/cql/client/peer_discovery.rb +33 -0
- data/lib/cql/client/query_result.rb +124 -1
- data/lib/cql/client/request_runner.rb +4 -2
- data/lib/cql/client/synchronous_client.rb +14 -2
- data/lib/cql/client/synchronous_prepared_statement.rb +19 -1
- data/lib/cql/future.rb +97 -50
- data/lib/cql/io/connection.rb +0 -1
- data/lib/cql/io/io_reactor.rb +1 -1
- data/lib/cql/protocol.rb +8 -1
- data/lib/cql/protocol/cql_protocol_handler.rb +2 -2
- data/lib/cql/protocol/decoding.rb +10 -15
- data/lib/cql/protocol/frame_decoder.rb +2 -1
- data/lib/cql/protocol/frame_encoder.rb +5 -4
- data/lib/cql/protocol/requests/auth_response_request.rb +31 -0
- data/lib/cql/protocol/requests/batch_request.rb +59 -0
- data/lib/cql/protocol/requests/credentials_request.rb +1 -1
- data/lib/cql/protocol/requests/execute_request.rb +45 -17
- data/lib/cql/protocol/requests/options_request.rb +1 -1
- data/lib/cql/protocol/requests/prepare_request.rb +1 -1
- data/lib/cql/protocol/requests/query_request.rb +97 -5
- data/lib/cql/protocol/requests/register_request.rb +1 -1
- data/lib/cql/protocol/requests/startup_request.rb +4 -4
- data/lib/cql/protocol/response.rb +2 -2
- data/lib/cql/protocol/responses/auth_challenge_response.rb +25 -0
- data/lib/cql/protocol/responses/auth_success_response.rb +25 -0
- data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
- data/lib/cql/protocol/responses/detailed_error_response.rb +1 -1
- data/lib/cql/protocol/responses/error_response.rb +3 -2
- data/lib/cql/protocol/responses/event_response.rb +3 -2
- data/lib/cql/protocol/responses/prepared_result_response.rb +10 -6
- data/lib/cql/protocol/responses/raw_rows_result_response.rb +27 -0
- data/lib/cql/protocol/responses/ready_response.rb +1 -1
- data/lib/cql/protocol/responses/result_response.rb +2 -2
- data/lib/cql/protocol/responses/rows_result_response.rb +43 -23
- data/lib/cql/protocol/responses/schema_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/schema_change_result_response.rb +1 -1
- data/lib/cql/protocol/responses/set_keyspace_result_response.rb +1 -1
- data/lib/cql/protocol/responses/status_change_event_response.rb +1 -1
- data/lib/cql/protocol/responses/supported_response.rb +1 -1
- data/lib/cql/protocol/responses/void_result_response.rb +1 -1
- data/lib/cql/protocol/type_converter.rb +2 -2
- data/lib/cql/uuid.rb +2 -2
- data/lib/cql/version.rb +1 -1
- data/spec/cql/client/asynchronous_client_spec.rb +493 -50
- data/spec/cql/client/asynchronous_prepared_statement_spec.rb +193 -11
- data/spec/cql/client/authenticators_spec.rb +56 -0
- data/spec/cql/client/batch_spec.rb +277 -0
- data/spec/cql/client/connector_spec.rb +606 -0
- data/spec/cql/client/execute_options_decoder_spec.rb +95 -0
- data/spec/cql/client/keyspace_changer_spec.rb +8 -8
- data/spec/cql/client/peer_discovery_spec.rb +92 -0
- data/spec/cql/client/query_result_spec.rb +352 -0
- data/spec/cql/client/request_runner_spec.rb +31 -5
- data/spec/cql/client/synchronous_client_spec.rb +44 -1
- data/spec/cql/client/synchronous_prepared_statement_spec.rb +63 -1
- data/spec/cql/future_spec.rb +50 -2
- data/spec/cql/protocol/cql_protocol_handler_spec.rb +16 -5
- data/spec/cql/protocol/decoding_spec.rb +16 -6
- data/spec/cql/protocol/encoding_spec.rb +3 -1
- data/spec/cql/protocol/frame_encoder_spec.rb +99 -50
- data/spec/cql/protocol/requests/auth_response_request_spec.rb +62 -0
- data/spec/cql/protocol/requests/batch_request_spec.rb +155 -0
- data/spec/cql/protocol/requests/credentials_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/execute_request_spec.rb +184 -71
- data/spec/cql/protocol/requests/options_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/prepare_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/query_request_spec.rb +255 -32
- data/spec/cql/protocol/requests/register_request_spec.rb +1 -1
- data/spec/cql/protocol/requests/startup_request_spec.rb +12 -6
- data/spec/cql/protocol/responses/auth_challenge_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/auth_success_response_spec.rb +31 -0
- data/spec/cql/protocol/responses/authenticate_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/detailed_error_response_spec.rb +14 -7
- data/spec/cql/protocol/responses/error_response_spec.rb +4 -2
- data/spec/cql/protocol/responses/event_response_spec.rb +7 -4
- data/spec/cql/protocol/responses/prepared_result_response_spec.rb +89 -34
- data/spec/cql/protocol/responses/raw_rows_result_response_spec.rb +66 -0
- data/spec/cql/protocol/responses/ready_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/result_response_spec.rb +19 -7
- data/spec/cql/protocol/responses/rows_result_response_spec.rb +56 -11
- data/spec/cql/protocol/responses/schema_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/schema_change_result_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/set_keyspace_result_response_spec.rb +1 -1
- data/spec/cql/protocol/responses/status_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/supported_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/topology_change_event_response_spec.rb +2 -1
- data/spec/cql/protocol/responses/void_result_response_spec.rb +1 -1
- data/spec/cql/protocol/type_converter_spec.rb +21 -4
- data/spec/cql/uuid_spec.rb +10 -3
- data/spec/integration/client_spec.rb +251 -28
- data/spec/integration/protocol_spec.rb +213 -62
- data/spec/integration/regression_spec.rb +4 -1
- data/spec/integration/uuid_spec.rb +4 -1
- data/spec/support/fake_io_reactor.rb +5 -5
- metadata +36 -7
- data/lib/cql/client/connection_helper.rb +0 -181
- data/spec/cql/client/connection_helper_spec.rb +0 -429
@@ -0,0 +1,606 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
|
6
|
+
module Cql
|
7
|
+
module Client
|
8
|
+
describe ClusterConnector do
|
9
|
+
let :cluster_connector do
|
10
|
+
described_class.new(node_connector, logger)
|
11
|
+
end
|
12
|
+
|
13
|
+
let :node_connector do
|
14
|
+
double(:node_connector)
|
15
|
+
end
|
16
|
+
|
17
|
+
let :logger do
|
18
|
+
NullLogger.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_connection(host)
|
22
|
+
c = double(:connection)
|
23
|
+
c.stub(:[]) do |key|
|
24
|
+
case key
|
25
|
+
when :data_center then 'dc'
|
26
|
+
when :host_id then Uuid.new('11111111-1111-1111-1111-111111111111')
|
27
|
+
else nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
c.stub(:host).and_return(host)
|
31
|
+
c.stub(:port).and_return(9999)
|
32
|
+
c.stub(:on_closed) do |&listener|
|
33
|
+
c.stub(:closed_listener).and_return(listener)
|
34
|
+
end
|
35
|
+
c.stub(:connected?).and_return(true)
|
36
|
+
c
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#connect_all' do
|
40
|
+
let :connections do
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
|
44
|
+
let :bad_nodes do
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
let :failure do
|
49
|
+
[StandardError.new('bork')]
|
50
|
+
end
|
51
|
+
|
52
|
+
before do
|
53
|
+
node_connector.stub(:connect) do |host|
|
54
|
+
connections << host
|
55
|
+
if bad_nodes.include?(host)
|
56
|
+
Future.failed(failure[0])
|
57
|
+
else
|
58
|
+
Future.resolved(make_connection(host))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'connects to each host' do
|
64
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
65
|
+
f.value
|
66
|
+
connections.should include('host0', 'host1')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'connects multiple times to each node' do
|
70
|
+
f = cluster_connector.connect_all(%w[host0 host1], 3)
|
71
|
+
f.value
|
72
|
+
connections.count { |c| c == 'host0' }.should == 3
|
73
|
+
connections.count { |c| c == 'host1' }.should == 3
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns a future that resolves to the connections' do
|
77
|
+
f = cluster_connector.connect_all(%w[host0 host1], 2)
|
78
|
+
f.value.map(&:host).sort.should == %w[host0 host0 host1 host1]
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'succeeds as long as one connection succeeds' do
|
82
|
+
bad_nodes.push('host0')
|
83
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
84
|
+
f.value.map(&:host).sort.should == %w[host1]
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'fails when all connections fail' do
|
88
|
+
bad_nodes.push('host0')
|
89
|
+
bad_nodes.push('host1')
|
90
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
91
|
+
expect { f.value }.to raise_error
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'fails with an AuthenticationError when the connections failed with a QueryError with error code 0x100' do
|
95
|
+
bad_nodes.push('host0')
|
96
|
+
bad_nodes.push('host1')
|
97
|
+
failure[0] = QueryError.new(0x100, 'bork')
|
98
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
99
|
+
expect { f.value }.to raise_error(AuthenticationError)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'logs when a connection is complete' do
|
103
|
+
logger.stub(:info)
|
104
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
105
|
+
f.value
|
106
|
+
logger.should have_received(:info).with(/connected to node .{36} at host0:9999 in data center dc/i)
|
107
|
+
logger.should have_received(:info).with(/connected to node .{36} at host1:9999 in data center dc/i)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'logs when a connection fails' do
|
111
|
+
logger.stub(:warn)
|
112
|
+
bad_nodes.push('host0')
|
113
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
114
|
+
f.value
|
115
|
+
logger.should have_received(:warn).with(/failed connecting to node at host0: bork/i)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'registers a listener that logs when a connection closes' do
|
119
|
+
logger.stub(:info)
|
120
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
121
|
+
connection = f.value.first
|
122
|
+
connection.closed_listener.call
|
123
|
+
logger.should have_received(:info).with(/connection to node .{36} at host0:9999 in data center dc closed/i)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'registers a listener that logs when a connection closes unexpectedly' do
|
127
|
+
logger.stub(:warn)
|
128
|
+
f = cluster_connector.connect_all(%w[host0 host1], 1)
|
129
|
+
connection = f.value.first
|
130
|
+
connection.closed_listener.call(StandardError.new('BORK'))
|
131
|
+
logger.should have_received(:warn).with(/connection to node .{36} at host0:9999 in data center dc closed unexpectedly: BORK/i)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe Connector do
|
137
|
+
let :connector do
|
138
|
+
described_class.new(steps)
|
139
|
+
end
|
140
|
+
|
141
|
+
let :steps do
|
142
|
+
[
|
143
|
+
double(:step0),
|
144
|
+
double(:step1),
|
145
|
+
double(:step2),
|
146
|
+
]
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#connect' do
|
150
|
+
it 'calls the first step with an object that has the connection parameters' do
|
151
|
+
steps[0].stub(:run) do |arg|
|
152
|
+
steps[0].stub(:arg).and_return(arg)
|
153
|
+
Future.resolved(arg)
|
154
|
+
end
|
155
|
+
connector = described_class.new(steps.take(1))
|
156
|
+
result = connector.connect('host0')
|
157
|
+
steps[0].arg.host.should == 'host0'
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'expects the last step to return a future that resolves to an object that has a connection' do
|
161
|
+
steps[0].stub(:run) do |arg|
|
162
|
+
Future.resolved(double(connection: :fake_connection))
|
163
|
+
end
|
164
|
+
connector = described_class.new(steps.take(1))
|
165
|
+
result = connector.connect('host0')
|
166
|
+
result.value.should == :fake_connection
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'passes the result of a step as argument to the next step' do
|
170
|
+
steps[0].stub(:run) do |arg|
|
171
|
+
Future.resolved(:foo)
|
172
|
+
end
|
173
|
+
steps[1].stub(:run) do |arg|
|
174
|
+
steps[1].stub(:arg).and_return(arg)
|
175
|
+
Future.resolved(:bar)
|
176
|
+
end
|
177
|
+
steps[2].stub(:run) do |arg|
|
178
|
+
steps[2].stub(:arg).and_return(arg)
|
179
|
+
Future.resolved(double(connection: :fake_connection))
|
180
|
+
end
|
181
|
+
connector = described_class.new(steps)
|
182
|
+
result = connector.connect('host0')
|
183
|
+
steps[1].arg.should == :foo
|
184
|
+
steps[2].arg.should == :bar
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'fails if any of the steps fail' do
|
188
|
+
steps[0].stub(:run) do |arg|
|
189
|
+
Future.resolved(:foo)
|
190
|
+
end
|
191
|
+
steps[1].stub(:run) do |arg|
|
192
|
+
raise 'bork'
|
193
|
+
end
|
194
|
+
steps[2].stub(:run) do |arg|
|
195
|
+
Future.resolved(double(connection: :fake_connection))
|
196
|
+
end
|
197
|
+
connector = described_class.new(steps)
|
198
|
+
result = connector.connect('host0')
|
199
|
+
expect { result.value }.to raise_error('bork')
|
200
|
+
steps[2].should_not have_received(:run)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe ConnectStep do
|
206
|
+
let :step do
|
207
|
+
described_class.new(io_reactor, 1111, 9, logger)
|
208
|
+
end
|
209
|
+
|
210
|
+
let :pending_connection do
|
211
|
+
double(:pending_connection)
|
212
|
+
end
|
213
|
+
|
214
|
+
let :new_pending_connection do
|
215
|
+
double(:new_pending_connection)
|
216
|
+
end
|
217
|
+
|
218
|
+
let :io_reactor do
|
219
|
+
double(:io_reactor)
|
220
|
+
end
|
221
|
+
|
222
|
+
let :logger do
|
223
|
+
NullLogger.new
|
224
|
+
end
|
225
|
+
|
226
|
+
let :connection do
|
227
|
+
double(:connection)
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#run' do
|
231
|
+
before do
|
232
|
+
pending_connection.stub(:host).and_return('example.com')
|
233
|
+
pending_connection.stub(:with_connection).and_return(new_pending_connection)
|
234
|
+
io_reactor.stub(:connect).and_return(Future.resolved(connection))
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'connects using the connection details given' do
|
238
|
+
step.run(pending_connection)
|
239
|
+
io_reactor.should have_received(:connect).with('example.com', 1111, 9)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'appends the connection to the given argument and returns the result' do
|
243
|
+
result = step.run(pending_connection)
|
244
|
+
result.value.should equal(new_pending_connection)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'logs a message when it starts connecting' do
|
248
|
+
logger.stub(:debug)
|
249
|
+
step.run(pending_connection)
|
250
|
+
logger.should have_received(:debug).with(/connecting to node at example\.com:1111/i)
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'returns a failed future when the connection fails' do
|
254
|
+
io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
|
255
|
+
result = step.run(pending_connection)
|
256
|
+
expect { result.value }.to raise_error('bork')
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
describe CacheOptionsStep do
|
262
|
+
let :step do
|
263
|
+
described_class.new
|
264
|
+
end
|
265
|
+
|
266
|
+
let :pending_connection do
|
267
|
+
double(:pending_connection)
|
268
|
+
end
|
269
|
+
|
270
|
+
describe '#run' do
|
271
|
+
before do
|
272
|
+
response = {'CQL_VERSION' => %w[3.1.2], 'COMPRESSION' => %w[snappy magic]}
|
273
|
+
pending_connection.stub(:execute).with(an_instance_of(Protocol::OptionsRequest)).and_return(Future.resolved(response))
|
274
|
+
pending_connection.stub(:[]=)
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'runs an OPTIONS request and adds the CQL version and compression support to the connection metadata' do
|
278
|
+
step.run(pending_connection)
|
279
|
+
pending_connection.should have_received(:[]=).with(:cql_version, %w[3.1.2])
|
280
|
+
pending_connection.should have_received(:[]=).with(:compression, %w[snappy magic])
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'returns the same argument as it was given' do
|
284
|
+
step.run(pending_connection).value.should equal(pending_connection)
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'returns a failed future when the request fails' do
|
288
|
+
pending_connection.stub(:execute).and_return(Future.failed(StandardError.new('bork')))
|
289
|
+
result = step.run(pending_connection)
|
290
|
+
expect { result.value }.to raise_error('bork')
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe InitializeStep do
|
296
|
+
let :step do
|
297
|
+
described_class.new('3.3.3', compressor, logger)
|
298
|
+
end
|
299
|
+
|
300
|
+
let :compressor do
|
301
|
+
nil
|
302
|
+
end
|
303
|
+
|
304
|
+
let :logger do
|
305
|
+
NullLogger.new
|
306
|
+
end
|
307
|
+
|
308
|
+
let :pending_connection do
|
309
|
+
double(:pending_connection)
|
310
|
+
end
|
311
|
+
|
312
|
+
describe '#run' do
|
313
|
+
before do
|
314
|
+
pending_connection.stub(:[]).with(:compression).and_return(%w[magic snappy])
|
315
|
+
pending_connection.stub(:execute) do |request|
|
316
|
+
pending_connection.stub(:last_request).and_return(request)
|
317
|
+
Future.resolved
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'sends a STARTUP request' do
|
322
|
+
step.run(pending_connection)
|
323
|
+
pending_connection.last_request.should be_a(Protocol::StartupRequest)
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'sets the CQL version' do
|
327
|
+
step.run(pending_connection)
|
328
|
+
pending_connection.last_request.options.should include('CQL_VERSION' => '3.3.3')
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'does not set the compression option when there is no compressor' do
|
332
|
+
step.run(pending_connection)
|
333
|
+
pending_connection.last_request.options.should_not have_key('COMPRESSION')
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'returns the same argument as it was given' do
|
337
|
+
step.run(pending_connection).value.should equal(pending_connection)
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'returns a failed future when the request fails' do
|
341
|
+
pending_connection.stub(:execute).and_return(Future.failed(StandardError.new('bork')))
|
342
|
+
result = step.run(pending_connection)
|
343
|
+
expect { result.value }.to raise_error('bork')
|
344
|
+
end
|
345
|
+
|
346
|
+
context 'when a compressor is given' do
|
347
|
+
let :compressor do
|
348
|
+
double(:compressor, algorithm: 'magic')
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'sets the compression option to the algorithm supported by the compressor' do
|
352
|
+
step.run(pending_connection)
|
353
|
+
pending_connection.last_request.options.should include('COMPRESSION' => 'magic')
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'does not set the compression option when the algorithm is not supported by the server' do
|
357
|
+
compressor.stub(:algorithm).and_return('bogus')
|
358
|
+
step.run(pending_connection)
|
359
|
+
pending_connection.last_request.options.should_not have_key('COMPRESSION')
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'logs a message when the server supports the algorithm' do
|
363
|
+
logger.stub(:debug)
|
364
|
+
step.run(pending_connection)
|
365
|
+
logger.should have_received(:debug).with(/using "magic" compression/i)
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'logs a message when the algorithm is not supported by the server' do
|
369
|
+
pending_connection.stub(:[]).with(:compression).and_return(%w[foo bar])
|
370
|
+
logger.stub(:warn)
|
371
|
+
step.run(pending_connection)
|
372
|
+
logger.should have_received(:warn).with(/algorithm "magic" not supported/i)
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'logs which algorithms the server supports when there is a mismatch' do
|
376
|
+
pending_connection.stub(:[]).with(:compression).and_return(%w[foo bar])
|
377
|
+
logger.stub(:warn)
|
378
|
+
step.run(pending_connection)
|
379
|
+
logger.should have_received(:warn).with(/server supports "foo", "bar"/i)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'when authentication is required' do
|
384
|
+
let :new_pending_connection do
|
385
|
+
double(:new_pending_connection)
|
386
|
+
end
|
387
|
+
|
388
|
+
before do
|
389
|
+
pending_connection.stub(:execute).and_return(Future.resolved(AuthenticationRequired.new('net.acme.Bogus')))
|
390
|
+
pending_connection.stub(:with_authentication_class).and_return(new_pending_connection)
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'appends the authentication class returned by the server to given argument and returns the result' do
|
394
|
+
step.run(pending_connection).value.should equal(new_pending_connection)
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
describe SaslAuthenticationStep do
|
401
|
+
let :step do
|
402
|
+
described_class.new(auth_provider)
|
403
|
+
end
|
404
|
+
|
405
|
+
let :auth_provider do
|
406
|
+
double(:auth_provider)
|
407
|
+
end
|
408
|
+
|
409
|
+
let :authenticator do
|
410
|
+
double(:authenticator)
|
411
|
+
end
|
412
|
+
|
413
|
+
let :pending_connection do
|
414
|
+
double(:pending_connection)
|
415
|
+
end
|
416
|
+
|
417
|
+
describe '#run' do
|
418
|
+
before do
|
419
|
+
pending_connection.stub(:authentication_class).and_return('org.acme.Auth')
|
420
|
+
pending_connection.stub(:execute).and_return(Future.resolved)
|
421
|
+
auth_provider.stub(:create_authenticator).and_return(authenticator)
|
422
|
+
authenticator.stub(:initial_response).and_return('fooblaha')
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'returns the pending connection when there\'s no authentication class' do
|
426
|
+
pending_connection.stub(:authentication_class).and_return(nil)
|
427
|
+
result = step.run(pending_connection)
|
428
|
+
result.value.should equal(pending_connection)
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'returns a failed future when there\'s an authentication class but no auth provider' do
|
432
|
+
step = described_class.new(nil)
|
433
|
+
result = step.run(pending_connection)
|
434
|
+
expect { result.value }.to raise_error(AuthenticationError)
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'returns a failed future when the auth provider does not support the authentication class' do
|
438
|
+
auth_provider.stub(:create_authenticator).and_return(nil)
|
439
|
+
result = step.run(pending_connection)
|
440
|
+
expect { result.value }.to raise_error(AuthenticationError)
|
441
|
+
end
|
442
|
+
|
443
|
+
it 'returns a failed future when the auth provider raises an error' do
|
444
|
+
auth_provider.stub(:create_authenticator).and_raise(StandardError.new('BORK'))
|
445
|
+
result = step.run(pending_connection)
|
446
|
+
expect { result.value }.to raise_error(AuthenticationError)
|
447
|
+
end
|
448
|
+
|
449
|
+
it 'asks the authenticator to formulate its initial response, and sends it in a AuthResponseRequest' do
|
450
|
+
pending_connection.stub(:execute) do |request|
|
451
|
+
pending_connection.stub(:last_request).and_return(request)
|
452
|
+
Future.resolved
|
453
|
+
end
|
454
|
+
step.run(pending_connection)
|
455
|
+
pending_connection.last_request.should == Protocol::AuthResponseRequest.new('fooblaha')
|
456
|
+
end
|
457
|
+
|
458
|
+
context 'with multiple challenges and responses' do
|
459
|
+
let :step do
|
460
|
+
described_class.new(auth_provider)
|
461
|
+
end
|
462
|
+
|
463
|
+
before do
|
464
|
+
authenticator.stub(:initial_response).and_return('1')
|
465
|
+
authenticator.stub(:challenge_response).with('2').and_return('3')
|
466
|
+
authenticator.stub(:challenge_response).with('4').and_return('5')
|
467
|
+
authenticator.stub(:authentication_successful)
|
468
|
+
end
|
469
|
+
|
470
|
+
before do
|
471
|
+
pending_connection.stub(:execute) do |request, &transform|
|
472
|
+
if request.token == '1'
|
473
|
+
response = Protocol::AuthChallengeResponse.new('2')
|
474
|
+
elsif request.token == '3'
|
475
|
+
response = Protocol::AuthChallengeResponse.new('4')
|
476
|
+
elsif request.token == '5'
|
477
|
+
response = Protocol::AuthSuccessResponse.new('6')
|
478
|
+
end
|
479
|
+
Future.resolved(transform.call(response))
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
it 'asks the authenticator to respond to challenges, sends the response in AuthResponseRequest:s until an AuthSuccess is returned' do
|
484
|
+
step.run(pending_connection)
|
485
|
+
authenticator.should have_received(:authentication_successful).with('6')
|
486
|
+
end
|
487
|
+
|
488
|
+
it 'handles client side failures in the middle of a challenge/response cycle' do
|
489
|
+
authenticator.stub(:challenge_response).with('4').and_raise(StandardError.new('BORK'))
|
490
|
+
f = step.run(pending_connection)
|
491
|
+
expect { f.value }.to raise_error('BORK')
|
492
|
+
end
|
493
|
+
|
494
|
+
it 'handles server side failures in the middle of a challenge/response cycle' do
|
495
|
+
pending_connection.stub(:execute) do |request, &transform|
|
496
|
+
if request.token == '1'
|
497
|
+
Future.resolved(Protocol::AuthChallengeResponse.new('2'))
|
498
|
+
else
|
499
|
+
Future.failed(QueryError.new(0x99, 'BORK'))
|
500
|
+
end
|
501
|
+
end
|
502
|
+
f = step.run(pending_connection)
|
503
|
+
expect { f.value }.to raise_error('BORK')
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'returns the same argument as it was given' do
|
508
|
+
result = step.run(pending_connection)
|
509
|
+
result.value.should equal(pending_connection)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
describe CredentialsAuthenticationStep do
|
515
|
+
let :step do
|
516
|
+
described_class.new(username: 'hello', password: 'world')
|
517
|
+
end
|
518
|
+
|
519
|
+
let :pending_connection do
|
520
|
+
double(:pending_connection)
|
521
|
+
end
|
522
|
+
|
523
|
+
describe '#run' do
|
524
|
+
before do
|
525
|
+
pending_connection.stub(:authentication_class).and_return('org.acme.Auth')
|
526
|
+
pending_connection.stub(:execute) do |request|
|
527
|
+
pending_connection.stub(:last_request).and_return(request)
|
528
|
+
Future.resolved
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
it 'sends a CredentialsRequest with the provided credentials' do
|
533
|
+
step.run(pending_connection)
|
534
|
+
pending_connection.last_request.should == Protocol::CredentialsRequest.new(username: 'hello', password: 'world')
|
535
|
+
end
|
536
|
+
|
537
|
+
it 'returns the pending connection when there\'s no authentication class' do
|
538
|
+
pending_connection.stub(:authentication_class).and_return(nil)
|
539
|
+
result = step.run(pending_connection)
|
540
|
+
result.value.should equal(pending_connection)
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'returns a failed future when there\'s an authentication class but no credentials' do
|
544
|
+
step = described_class.new(nil)
|
545
|
+
result = step.run(pending_connection)
|
546
|
+
expect { result.value }.to raise_error(AuthenticationError)
|
547
|
+
end
|
548
|
+
|
549
|
+
it 'returns a failed future when the server responds with an error' do
|
550
|
+
pending_connection.stub(:execute).and_return(Future.failed(QueryError.new(0x99, 'BORK')))
|
551
|
+
result = step.run(pending_connection)
|
552
|
+
expect { result.value }.to raise_error('BORK')
|
553
|
+
end
|
554
|
+
|
555
|
+
it 'returns the same argument as it was given' do
|
556
|
+
result = step.run(pending_connection)
|
557
|
+
result.value.should equal(pending_connection)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
describe CachePropertiesStep do
|
563
|
+
let :step do
|
564
|
+
described_class.new
|
565
|
+
end
|
566
|
+
|
567
|
+
let :pending_connection do
|
568
|
+
double(:pending_connection)
|
569
|
+
end
|
570
|
+
|
571
|
+
describe '#run' do
|
572
|
+
before do
|
573
|
+
node_info = {'data_center' => 'dc', 'host_id' => Uuid.new('11111111-1111-1111-1111-111111111111')}
|
574
|
+
pending_connection.stub(:execute) do |request|
|
575
|
+
pending_connection.stub(:last_request).and_return(request)
|
576
|
+
Future.resolved(QueryResult.new([], [node_info], nil, nil))
|
577
|
+
end
|
578
|
+
pending_connection.stub(:[]=)
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'queries the system table "local" for data center and host ID and adds these to the connection metadata' do
|
582
|
+
step.run(pending_connection)
|
583
|
+
pending_connection.should have_received(:[]=).with(:data_center, 'dc')
|
584
|
+
pending_connection.should have_received(:[]=).with(:host_id, Uuid.new('11111111-1111-1111-1111-111111111111'))
|
585
|
+
end
|
586
|
+
|
587
|
+
it 'handles the case when the query result is empty' do
|
588
|
+
pending_connection.stub(:execute).and_return(Future.resolved(QueryResult.new([], [], nil, nil)))
|
589
|
+
result = step.run(pending_connection)
|
590
|
+
result.should be_resolved
|
591
|
+
pending_connection.should_not have_received(:[]=)
|
592
|
+
end
|
593
|
+
|
594
|
+
it 'returns the same argument as it was given' do
|
595
|
+
step.run(pending_connection).value.should equal(pending_connection)
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'returns a failed future when the request fails' do
|
599
|
+
pending_connection.stub(:execute).and_return(Future.failed(StandardError.new('bork')))
|
600
|
+
result = step.run(pending_connection)
|
601
|
+
expect { result.value }.to raise_error('bork')
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|