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