cql-rb 1.1.0.pre3 → 1.1.0.pre6

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 (42) hide show
  1. data/README.md +2 -2
  2. data/lib/cql/client.rb +9 -5
  3. data/lib/cql/client/asynchronous_client.rb +105 -192
  4. data/lib/cql/client/asynchronous_prepared_statement.rb +51 -9
  5. data/lib/cql/client/connection_helper.rb +155 -0
  6. data/lib/cql/client/connection_manager.rb +56 -0
  7. data/lib/cql/client/keyspace_changer.rb +27 -0
  8. data/lib/cql/client/null_logger.rb +21 -0
  9. data/lib/cql/client/request_runner.rb +5 -3
  10. data/lib/cql/client/synchronous_client.rb +5 -5
  11. data/lib/cql/client/synchronous_prepared_statement.rb +4 -8
  12. data/lib/cql/future.rb +320 -210
  13. data/lib/cql/io/connection.rb +5 -5
  14. data/lib/cql/io/io_reactor.rb +21 -23
  15. data/lib/cql/protocol/cql_protocol_handler.rb +69 -38
  16. data/lib/cql/protocol/encoding.rb +5 -1
  17. data/lib/cql/protocol/requests/register_request.rb +2 -0
  18. data/lib/cql/protocol/type_converter.rb +1 -0
  19. data/lib/cql/version.rb +1 -1
  20. data/spec/cql/client/asynchronous_client_spec.rb +368 -175
  21. data/spec/cql/client/asynchronous_prepared_statement_spec.rb +132 -22
  22. data/spec/cql/client/connection_helper_spec.rb +335 -0
  23. data/spec/cql/client/connection_manager_spec.rb +118 -0
  24. data/spec/cql/client/keyspace_changer_spec.rb +50 -0
  25. data/spec/cql/client/request_runner_spec.rb +12 -12
  26. data/spec/cql/client/synchronous_client_spec.rb +15 -15
  27. data/spec/cql/client/synchronous_prepared_statement_spec.rb +15 -11
  28. data/spec/cql/future_spec.rb +529 -301
  29. data/spec/cql/io/connection_spec.rb +12 -12
  30. data/spec/cql/io/io_reactor_spec.rb +61 -61
  31. data/spec/cql/protocol/cql_protocol_handler_spec.rb +26 -12
  32. data/spec/cql/protocol/encoding_spec.rb +5 -0
  33. data/spec/cql/protocol/type_converter_spec.rb +1 -1
  34. data/spec/cql/time_uuid_spec.rb +7 -7
  35. data/spec/integration/client_spec.rb +2 -2
  36. data/spec/integration/io_spec.rb +20 -20
  37. data/spec/integration/protocol_spec.rb +17 -17
  38. data/spec/integration/regression_spec.rb +6 -0
  39. data/spec/integration/uuid_spec.rb +4 -0
  40. data/spec/support/fake_io_reactor.rb +38 -8
  41. data/spec/support/fake_server.rb +3 -3
  42. metadata +12 -2
@@ -6,16 +6,12 @@ require 'spec_helper'
6
6
  module Cql
7
7
  module Client
8
8
  describe AsynchronousPreparedStatement do
9
- let :statement do
10
- described_class.new(connection, statement_id, raw_metadata)
9
+ let :connection_manager do
10
+ ConnectionManager.new
11
11
  end
12
12
 
13
- let :connection do
14
- stub(:connection)
15
- end
16
-
17
- let :statement_id do
18
- "\x2a"
13
+ let :logger do
14
+ NullLogger.new
19
15
  end
20
16
 
21
17
  let :raw_metadata do
@@ -33,7 +29,72 @@ module Cql
33
29
  ]
34
30
  end
35
31
 
32
+ let :cql do
33
+ 'SELECT * FROM my_table'
34
+ end
35
+
36
+ let :connections do
37
+ [
38
+ FakeConnection.new('h0.example.com', 1234, 42),
39
+ FakeConnection.new('h1.example.com', 1234, 42),
40
+ FakeConnection.new('h2.example.com', 1234, 42),
41
+ ]
42
+ end
43
+
44
+ def handle_request(connection, request)
45
+ case request
46
+ when Protocol::PrepareRequest
47
+ statement_id = [rand(2**31)].pack('c*')
48
+ connection[:last_prepared_statement_id] = statement_id
49
+ Protocol::PreparedResultResponse.new(statement_id, raw_metadata)
50
+ when Protocol::ExecuteRequest
51
+ Protocol::RowsResultResponse.new(rows, raw_metadata)
52
+ else
53
+ raise %(Unexpected request: #{request})
54
+ end
55
+ end
56
+
57
+ before do
58
+ connections.each do |c|
59
+ c.handle_request { |r| handle_request(c, r) }
60
+ end
61
+ connection_manager.add_connections(connections)
62
+ end
63
+
64
+ describe '.prepare' do
65
+ it 'prepares a statement on all connections' do
66
+ f = described_class.prepare(cql, :one, connection_manager, logger)
67
+ f.value
68
+ connections.each do |c|
69
+ c.requests.should include(Protocol::PrepareRequest.new(cql))
70
+ end
71
+ end
72
+
73
+ it 'returns a prepared statement object' do
74
+ f = described_class.prepare(cql, :two, connection_manager, logger)
75
+ f.value.should be_a(PreparedStatement)
76
+ end
77
+
78
+ it 'returns a failed future when something goes wrong in the preparation' do
79
+ connections.each(&:close)
80
+ f = described_class.prepare(cql, :three, connection_manager, logger)
81
+ expect { f.value }.to raise_error(NotConnectedError)
82
+ end
83
+
84
+ it 'returns a failed future if the preparation results in an error' do
85
+ connections.each do |connection|
86
+ connection.stub(:send_request).and_return(Future.resolved(Protocol::ErrorResponse.new(99, 'bork')))
87
+ end
88
+ f = described_class.prepare(cql, :quorum, connection_manager, logger)
89
+ expect { f.value }.to raise_error('bork')
90
+ end
91
+ end
92
+
36
93
  describe '#metadata' do
94
+ let :statement do
95
+ described_class.prepare(cql, :all, connection_manager, logger).value
96
+ end
97
+
37
98
  it 'returns the interpreted metadata' do
38
99
  statement.metadata.should be_a(ResultMetadata)
39
100
  statement.metadata['my_column'].should be_a(ColumnMetadata)
@@ -41,28 +102,77 @@ module Cql
41
102
  end
42
103
 
43
104
  describe '#execute' do
44
- it 'creates and sends an EXECUTE request' do
45
- expected_request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :one)
46
- connection.should_receive(:send_request).with(expected_request)
47
- statement.execute(11, 'hello', :one)
105
+ let :statement do
106
+ described_class.prepare(cql, :local_quorum, connection_manager, logger).value
107
+ end
108
+
109
+ it 'executes itself on one of the connections' do
110
+ statement.execute(11, 'hello')
111
+ requests = connections.flat_map(&:requests).select { |r| r.is_a?(Protocol::ExecuteRequest) }
112
+ requests.should have(1).item
113
+ requests.first.metadata.should == raw_metadata
114
+ requests.first.values.should == [11, 'hello']
115
+ end
116
+
117
+ it 'uses the right statement ID for the connection' do
118
+ statement.execute(11, 'hello')
119
+ connection, request = connections.map { |c| [c, c.requests.find { |r| r.is_a?(Protocol::ExecuteRequest) }] }.find { |c, r| r }
120
+ request.id.should == connection[:last_prepared_statement_id]
121
+ end
122
+
123
+ it 'sends the default consistency level' do
124
+ statement.execute(11, 'hello')
125
+ request = connections.flat_map(&:requests).find { |r| r.is_a?(Protocol::ExecuteRequest) }
126
+ request.consistency.should == :local_quorum
127
+ end
128
+
129
+ it 'sends the consistency given as last argument' do
130
+ statement.execute(11, 'hello', :two)
131
+ request = connections.flat_map(&:requests).find { |r| r.is_a?(Protocol::ExecuteRequest) }
132
+ request.consistency.should == :two
133
+ end
134
+
135
+ context 'when it receives a new connection from the connection manager' do
136
+ let :new_connection do
137
+ FakeConnection.new('h3.example.com', 1234, 5)
138
+ end
139
+
140
+ before do
141
+ statement
142
+ new_connection.handle_request { |r| handle_request(new_connection, r) }
143
+ connections.each(&:close)
144
+ connection_manager.add_connections([new_connection])
145
+ end
146
+
147
+ it 'prepares itself on the connection' do
148
+ statement.execute(11, 'hello')
149
+ new_connection.requests.should include(Protocol::PrepareRequest.new(cql))
150
+ execute_request = new_connection.requests.find { |r| r.is_a?(Protocol::ExecuteRequest) }
151
+ execute_request.metadata.should == raw_metadata
152
+ execute_request.values.should == [11, 'hello']
153
+ end
154
+
155
+ it 'logs a message' do
156
+ logger.stub(:debug)
157
+ statement.execute(11, 'hello')
158
+ logger.should have_received(:debug).with(/Statement prepared/).once
159
+ end
48
160
  end
49
161
 
50
- it 'returns a future that resolves to a QueryResult' do
51
- request = Cql::Protocol::ExecuteRequest.new(statement_id, raw_metadata, [11, 'hello'], :two)
52
- response = Cql::Protocol::RowsResultResponse.new(rows, raw_metadata)
53
- connection.stub(:send_request).with(request).and_return(Future.completed(response))
54
- result = statement.execute(11, 'hello', :two).get
55
- result.metadata['my_other_column'].should == ColumnMetadata.new('my_keyspace', 'my_table', 'my_other_column', :text)
56
- result.first.should == {'my_column' => 11, 'my_other_column' => 'hello'}
162
+ it 'returns a future that resolves to the result' do
163
+ f = statement.execute(11, 'hello')
164
+ query_result = f.value
165
+ query_result.metadata['my_other_column'].should == ColumnMetadata.new('my_keyspace', 'my_table', 'my_other_column', :text)
166
+ query_result.first.should == rows.first
57
167
  end
58
168
 
59
169
  it 'returns a failed future when the number of arguments is wrong' do
60
170
  f1 = statement.execute(11, :one)
61
171
  f2 = statement.execute(11, 'foo', 22, :one)
62
- expect { f1.get }.to raise_error
63
- expect { f2.get }.to raise_error
172
+ expect { f1.value }.to raise_error
173
+ expect { f2.value }.to raise_error
64
174
  end
65
175
  end
66
176
  end
67
177
  end
68
- end
178
+ end
@@ -0,0 +1,335 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Client
8
+ describe ConnectionHelper do
9
+ let :connection_helper do
10
+ described_class.new(io_reactor, 9876, nil, 7, logger)
11
+ end
12
+
13
+ let :io_reactor do
14
+ double(:io_reactor)
15
+ end
16
+
17
+ let :logger do
18
+ NullLogger.new
19
+ end
20
+
21
+ describe '#connect' do
22
+ let :hosts do
23
+ %w[host0 host1]
24
+ end
25
+
26
+ let :local_metadata do
27
+ [
28
+ ['system', 'local', 'data_center', :text],
29
+ ['system', 'local', 'host_id', :uuid],
30
+ ]
31
+ end
32
+
33
+ before do
34
+ io_reactor.stub(:start).and_return(Future.resolved)
35
+ io_reactor.stub(:connect).and_return(Future.resolved)
36
+ end
37
+
38
+ it 'starts the IO reactor' do
39
+ connection_helper.connect(hosts, nil)
40
+ io_reactor.should have_received(:start)
41
+ end
42
+
43
+ it 'fails when the IO reactor fails to start' do
44
+ io_reactor.stub(:start).and_return(Future.failed(StandardError.new('bork')))
45
+ f = connection_helper.connect(hosts, nil)
46
+ expect { f.value }.to raise_error('bork')
47
+ end
48
+
49
+ it 'connects to the specified hosts' do
50
+ connection_helper.connect(hosts, nil)
51
+ io_reactor.should have_received(:connect).with('host0', 9876, 7)
52
+ io_reactor.should have_received(:connect).with('host1', 9876, 7)
53
+ end
54
+
55
+ it 'logs a message when a node connects' do
56
+ logger.stub(:info)
57
+ io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
58
+ connection_helper.connect(hosts, nil)
59
+ logger.should have_received(:info).with(/Connected to node/).exactly(hosts.size).times
60
+ end
61
+
62
+ it 'logs a message when connecting to a node' do
63
+ logger.stub(:debug)
64
+ io_reactor.stub(:connect).and_return(Future.resolved(FakeConnection.new('host', 9876, 7)))
65
+ connection_helper.connect(hosts, nil)
66
+ logger.should have_received(:debug).with(/Connecting to node/).exactly(hosts.size).times
67
+ end
68
+
69
+ it 'fails when all hosts fail to connect' do
70
+ io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
71
+ f = connection_helper.connect(hosts, nil)
72
+ expect { f.value }.to raise_error('bork')
73
+ end
74
+
75
+ it 'logs a message when a node fails to connect' do
76
+ logger.stub(:warn)
77
+ io_reactor.stub(:connect).and_return(Future.failed(StandardError.new('bork')))
78
+ connection_helper.connect(hosts, nil)
79
+ logger.should have_received(:warn).with(/Failed connecting to node/).exactly(hosts.size).times
80
+ end
81
+
82
+ it 'fails with an AuthenticationError when the connections fail to connect because of authentication issues' do
83
+ io_reactor.stub(:connect).and_return(Future.failed(QueryError.new(0x100, 'bork')))
84
+ f = connection_helper.connect(hosts, nil)
85
+ expect { f.value }.to raise_error(AuthenticationError)
86
+ end
87
+
88
+ it 'initializes the connections' do
89
+ connection0 = FakeConnection.new('host0', 9876, 7)
90
+ connection1 = FakeConnection.new('host1', 9876, 7)
91
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
92
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
93
+ connection_helper.connect(hosts, 'some_keyspace')
94
+ [connection0, connection1].each do |c|
95
+ c.requests[0].should be_a(Protocol::StartupRequest)
96
+ c.requests[1].cql.should match(/SELECT .* FROM system.local/)
97
+ c.requests[2].cql.should == 'USE some_keyspace'
98
+ end
99
+ end
100
+
101
+ it 'fails if authentication is required and no credentials were specified' do
102
+ connection = FakeConnection.new('host0', 9876, 7)
103
+ connection.handle_request do |request|
104
+ if request.is_a?(Protocol::StartupRequest)
105
+ Protocol::AuthenticateResponse.new('xyz')
106
+ else
107
+ connection.default_request_handler(request)
108
+ end
109
+ end
110
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
111
+ f = connection_helper.connect(hosts, nil)
112
+ expect { f.value }.to raise_error(AuthenticationError)
113
+ end
114
+
115
+ it 'authenticates when authentication is required and credentials were specified' do
116
+ credentials = {'username' => 'foo', 'password' => 'bar'}
117
+ connection_helper = described_class.new(io_reactor, 9876, credentials, 7, logger)
118
+ connection = FakeConnection.new('host0', 9876, 7)
119
+ authentication_sent = false
120
+ connection.handle_request do |request|
121
+ if request.is_a?(Protocol::StartupRequest)
122
+ Protocol::AuthenticateResponse.new('xyz')
123
+ elsif request == Protocol::CredentialsRequest.new(credentials)
124
+ authentication_sent = true
125
+ Protocol::ReadyResponse.new
126
+ else
127
+ connection.default_request_handler(request)
128
+ end
129
+ end
130
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
131
+ f = connection_helper.connect(hosts, nil)
132
+ f.value
133
+ authentication_sent.should be_true
134
+ end
135
+
136
+ it 'decorates the connections with :host_id and :data_center' do
137
+ connection = FakeConnection.new('host0', 9876, 7)
138
+ connection.handle_request do |request|
139
+ if request.is_a?(Protocol::QueryRequest) && request.cql =~ /SELECT .* FROM system\.local/
140
+ row = {'data_center' => 'dc1', 'host_id' => Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')}
141
+ Protocol::RowsResultResponse.new([row], local_metadata)
142
+ else
143
+ connection.default_request_handler(request)
144
+ end
145
+ end
146
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
147
+ connection_helper.connect(hosts, nil)
148
+ connection[:host_id].should == Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')
149
+ connection[:data_center].should == 'dc1'
150
+ end
151
+
152
+ it 'registers a close handler that logs when connections close' do
153
+ logger.stub(:warn)
154
+ connection = FakeConnection.new('host', 9876, 7)
155
+ io_reactor.stub(:connect).and_return(Future.resolved(connection))
156
+ connection_helper.connect(hosts.take(1), nil)
157
+ connection.close
158
+ logger.should have_received(:warn).with(/Connection to node .* closed/)
159
+ end
160
+
161
+ it 'initializes a peer discovery when connected to the specified hosts' do
162
+ connection_helper.stub(:discover_peers)
163
+ connection_helper.connect(hosts, nil)
164
+ connection0 = FakeConnection.new('host0', 9876, 7)
165
+ connection1 = FakeConnection.new('host1', 9876, 7)
166
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection0))
167
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.resolved(connection1))
168
+ connection_helper.connect(hosts, 'some_keyspace')
169
+ connection_helper.should have_received(:discover_peers).with([connection0, connection1], 'some_keyspace')
170
+ end
171
+
172
+ it 'initializes a peer discovery with the successfull connections as seeds' do
173
+ connection_helper.stub(:discover_peers)
174
+ connection_helper.connect(hosts, nil)
175
+ connection = FakeConnection.new('host0', 9876, 7)
176
+ io_reactor.stub(:connect).with('host0', 9876, 7).and_return(Future.resolved(connection))
177
+ io_reactor.stub(:connect).with('host1', 9876, 7).and_return(Future.failed(StandardError.new('bork')))
178
+ connection_helper.connect(hosts, 'some_keyspace')
179
+ connection_helper.should have_received(:discover_peers).with([connection], 'some_keyspace')
180
+ end
181
+ end
182
+
183
+ describe '#discover_peers' do
184
+ let :seed_connections do
185
+ [
186
+ FakeConnection.new('host0', 9042, 5),
187
+ FakeConnection.new('host1', 9042, 5),
188
+ FakeConnection.new('host2', 9042, 5),
189
+ ]
190
+ end
191
+
192
+ let :seed_connection_rows do
193
+ [
194
+ {'peer' => IPAddr.new('2.0.0.0'), 'rpc_address' => IPAddr.new('1.0.0.0'), 'data_center' => 'dc1', 'host_id' => Uuid.new('eac69196-1e28-11e3-8e2b-191b6d153d0c')},
195
+ {'peer' => IPAddr.new('2.0.0.1'), 'rpc_address' => IPAddr.new('1.0.0.1'), 'data_center' => 'dc1', 'host_id' => Uuid.new('fa5f9562-1e28-11e3-bf05-3d3a155d0608')},
196
+ {'peer' => IPAddr.new('2.0.0.2'), 'rpc_address' => IPAddr.new('1.0.0.2'), 'data_center' => 'dc1', 'host_id' => Uuid.new('018b8f1c-1e29-11e3-b14f-532d016437ce')},
197
+ ]
198
+ end
199
+
200
+ let :extra_connection_rows do
201
+ [
202
+ {'peer' => IPAddr.new('2.0.0.3'), 'rpc_address' => IPAddr.new('1.0.0.3'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7a3ccace-1e2a-11e3-a447-43312b1c66e4')},
203
+ {'peer' => IPAddr.new('2.0.0.4'), 'rpc_address' => IPAddr.new('1.0.0.4'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7bbd4e32-1e2a-11e3-b21d-69d7c02cece8')},
204
+ {'peer' => IPAddr.new('2.0.0.5'), 'rpc_address' => IPAddr.new('1.0.0.5'), 'data_center' => 'dc1', 'host_id' => Uuid.new('7d7e76f6-1e2a-11e3-bfa0-4fb416ef4064')},
205
+ ]
206
+ end
207
+
208
+ let :peer_metadata do
209
+ [
210
+ ['system', 'peers', 'peer', :inet],
211
+ ['system', 'peers', 'data_center', :varchar],
212
+ ['system', 'peers', 'host_id', :uuid],
213
+ ['system', 'peers', 'rpc_address', :inet],
214
+ ]
215
+ end
216
+
217
+ before do
218
+ seed_connections.each_with_index do |c, i|
219
+ c[:host_id] = seed_connection_rows[i]['host_id']
220
+ c[:data_center] = seed_connection_rows[i]['data_center']
221
+ end
222
+ end
223
+
224
+ def peer_request_response
225
+ seed_connections.each do |c|
226
+ c.handle_request do |request|
227
+ if request.cql =~ /SELECT .* FROM system\.peers/
228
+ Protocol::RowsResultResponse.new(yield, peer_metadata)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ it 'returns immediately if there are no seed connections' do
235
+ f = connection_helper.discover_peers([], nil)
236
+ f.value
237
+ end
238
+
239
+ it 'logs a message when it begins' do
240
+ logger.stub(:debug)
241
+ connection_helper.discover_peers([], nil)
242
+ logger.should have_received(:debug).with(/Looking for additional nodes/)
243
+ end
244
+
245
+ it 'asks a random connection for its peers' do
246
+ connection_helper.discover_peers(seed_connections, nil)
247
+ connection = seed_connections.find { |c| c.requests.any? }
248
+ connection.requests.first.cql.should match(/SELECT .* FROM system\.peers/)
249
+ end
250
+
251
+ it 'returns an empty list when it only finds nodes it\'s already connected to' do
252
+ peer_request_response { seed_connection_rows }
253
+ f = connection_helper.discover_peers(seed_connections, nil)
254
+ f.value.should be_empty
255
+ end
256
+
257
+ it 'logs a message when it finds no new nodes' do
258
+ logger.stub(:debug)
259
+ peer_request_response { seed_connection_rows }
260
+ connection_helper.discover_peers(seed_connections, nil)
261
+ logger.should have_received(:debug).with(/No additional nodes found/)
262
+ end
263
+
264
+ it 'returns an empty list when it only finds nodes data centers other than those of the seed connections' do
265
+ seed_connections[1][:data_center] = 'dc2'
266
+ seed_connection_rows[1]['data_center'] = 'dc2'
267
+ extra_connection_rows[0]['data_center'] = 'dc3'
268
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
269
+ f = connection_helper.discover_peers(seed_connections, nil)
270
+ f.value.should be_empty
271
+ end
272
+
273
+ it 'connects to the nodes it finds that it is not already connected to' do
274
+ connection = FakeConnection.new('host3', 9876, 7)
275
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
276
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
277
+ f = connection_helper.discover_peers(seed_connections, nil)
278
+ f.value
279
+ end
280
+
281
+ it 'logs the number of new nodes found' do
282
+ logger.stub(:debug)
283
+ connection = FakeConnection.new('host3', 9876, 7)
284
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
285
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
286
+ connection_helper.discover_peers(seed_connections, nil)
287
+ logger.should have_received(:debug).with(/1 additional nodes found/)
288
+ end
289
+
290
+ it 'returns the new connections' do
291
+ connection = FakeConnection.new('host3', 9876, 7)
292
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
293
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
294
+ f = connection_helper.discover_peers(seed_connections, nil)
295
+ f.value.should == [connection]
296
+ end
297
+
298
+ it 'initializes the new connections' do
299
+ connection = FakeConnection.new('host3', 9876, 7)
300
+ io_reactor.stub(:connect).with('1.0.0.3', 9876, 7).and_return(Future.resolved(connection))
301
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
302
+ f = connection_helper.discover_peers(seed_connections, 'some_keyspace')
303
+ f.value
304
+ connection.requests[0].should be_a(Protocol::StartupRequest)
305
+ connection.requests[1].cql.should match(/SELECT .* FROM system.local/)
306
+ connection.requests[2].cql.should == 'USE some_keyspace'
307
+ end
308
+
309
+ it 'connects only to node in the same data centers as the seed nodes' do
310
+ seed_connections[1][:data_center] = 'dc2'
311
+ seed_connection_rows[1]['data_center'] = 'dc2'
312
+ extra_connection_rows[0]['data_center'] = 'dc3'
313
+ extra_connection_rows[1]['data_center'] = 'dc2'
314
+ extra_connection_rows[2]['data_center'] = 'dc1'
315
+ connection4 = FakeConnection.new('host4', 9876, 7)
316
+ connection5 = FakeConnection.new('host5', 9876, 7)
317
+ io_reactor.stub(:connect).with('1.0.0.4', 9876, 7).and_return(Future.resolved(connection4))
318
+ io_reactor.stub(:connect).with('1.0.0.5', 9876, 7).and_return(Future.resolved(connection5))
319
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(3) }
320
+ f = connection_helper.discover_peers(seed_connections, nil)
321
+ f.value.should == [connection4, connection5]
322
+ end
323
+
324
+ it 'uses the peer address instead of the RPC address when latter is 0.0.0.0' do
325
+ extra_connection_rows[0]['rpc_address'] = IPAddr.new('0.0.0.0')
326
+ connection = FakeConnection.new('host3', 9876, 7)
327
+ io_reactor.stub(:connect).with(extra_connection_rows[0]['peer'].to_s, 9876, 7).and_return(Future.resolved(connection))
328
+ peer_request_response { seed_connection_rows + extra_connection_rows.take(1) }
329
+ f = connection_helper.discover_peers(seed_connections, nil)
330
+ f.value
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end