cql-rb 1.0.0.pre7 → 1.0.0.pre8

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 (54) hide show
  1. data/lib/cql/byte_buffer.rb +22 -0
  2. data/lib/cql/client.rb +79 -310
  3. data/lib/cql/client/asynchronous_client.rb +254 -0
  4. data/lib/cql/client/asynchronous_prepared_statement.rb +19 -0
  5. data/lib/cql/client/column_metadata.rb +22 -0
  6. data/lib/cql/client/query_result.rb +34 -0
  7. data/lib/cql/client/result_metadata.rb +31 -0
  8. data/lib/cql/client/synchronous_client.rb +47 -0
  9. data/lib/cql/client/synchronous_prepared_statement.rb +47 -0
  10. data/lib/cql/future.rb +7 -3
  11. data/lib/cql/io.rb +1 -0
  12. data/lib/cql/io/io_reactor.rb +9 -3
  13. data/lib/cql/io/node_connection.rb +2 -2
  14. data/lib/cql/protocol.rb +5 -4
  15. data/lib/cql/protocol/request.rb +23 -0
  16. data/lib/cql/protocol/requests/credentials_request.rb +1 -1
  17. data/lib/cql/protocol/requests/execute_request.rb +13 -84
  18. data/lib/cql/protocol/requests/options_request.rb +1 -1
  19. data/lib/cql/protocol/requests/prepare_request.rb +2 -1
  20. data/lib/cql/protocol/requests/query_request.rb +3 -1
  21. data/lib/cql/protocol/requests/register_request.rb +1 -1
  22. data/lib/cql/protocol/requests/startup_request.rb +1 -2
  23. data/lib/cql/protocol/{response_body.rb → response.rb} +1 -1
  24. data/lib/cql/protocol/responses/authenticate_response.rb +1 -1
  25. data/lib/cql/protocol/responses/error_response.rb +1 -1
  26. data/lib/cql/protocol/responses/ready_response.rb +1 -1
  27. data/lib/cql/protocol/responses/result_response.rb +1 -1
  28. data/lib/cql/protocol/responses/rows_result_response.rb +1 -1
  29. data/lib/cql/protocol/responses/supported_response.rb +1 -1
  30. data/lib/cql/protocol/type_converter.rb +226 -46
  31. data/lib/cql/version.rb +1 -1
  32. data/spec/cql/byte_buffer_spec.rb +38 -0
  33. data/spec/cql/client/asynchronous_client_spec.rb +472 -0
  34. data/spec/cql/client/client_shared.rb +27 -0
  35. data/spec/cql/client/synchronous_client_spec.rb +104 -0
  36. data/spec/cql/client/synchronous_prepared_statement_spec.rb +65 -0
  37. data/spec/cql/future_spec.rb +4 -0
  38. data/spec/cql/io/io_reactor_spec.rb +39 -20
  39. data/spec/cql/protocol/request_spec.rb +17 -0
  40. data/spec/cql/protocol/requests/credentials_request_spec.rb +82 -0
  41. data/spec/cql/protocol/requests/execute_request_spec.rb +174 -0
  42. data/spec/cql/protocol/requests/options_request_spec.rb +24 -0
  43. data/spec/cql/protocol/requests/prepare_request_spec.rb +70 -0
  44. data/spec/cql/protocol/requests/query_request_spec.rb +95 -0
  45. data/spec/cql/protocol/requests/register_request_spec.rb +24 -0
  46. data/spec/cql/protocol/requests/startup_request_spec.rb +29 -0
  47. data/spec/integration/client_spec.rb +26 -19
  48. data/spec/integration/protocol_spec.rb +2 -2
  49. data/spec/integration/regression_spec.rb +1 -1
  50. metadata +35 -9
  51. data/lib/cql/protocol/request_body.rb +0 -15
  52. data/lib/cql/protocol/request_frame.rb +0 -20
  53. data/spec/cql/client_spec.rb +0 -454
  54. data/spec/cql/protocol/request_frame_spec.rb +0 -456
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ shared_context 'client setup' do
4
+ let :connection_options do
5
+ {:host => 'example.com', :port => 12321, :io_reactor => io_reactor}
6
+ end
7
+
8
+ let :io_reactor do
9
+ FakeIoReactor.new
10
+ end
11
+
12
+ def connections
13
+ io_reactor.connections
14
+ end
15
+
16
+ def connection
17
+ connections.first
18
+ end
19
+
20
+ def requests
21
+ connection[:requests]
22
+ end
23
+
24
+ def last_request
25
+ requests.last
26
+ end
27
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'cql/client/client_shared'
5
+
6
+
7
+ module Cql
8
+ module Client
9
+ describe SynchronousClient do
10
+ let :client do
11
+ described_class.new(async_client)
12
+ end
13
+
14
+ let :async_client do
15
+ stub(:async_client)
16
+ end
17
+
18
+ let :future do
19
+ stub(:future, get: nil)
20
+ end
21
+
22
+ describe '#connect' do
23
+ it 'calls #connect on the async client and waits for the result' do
24
+ async_client.should_receive(:connect).and_return(future)
25
+ future.should_receive(:get)
26
+ client.connect
27
+ end
28
+
29
+ it 'returns self' do
30
+ async_client.stub(:connect).and_return(future)
31
+ client.connect.should equal(client)
32
+ end
33
+ end
34
+
35
+ describe '#close' do
36
+ it 'calls #close on the async client and waits for the result' do
37
+ async_client.should_receive(:close).and_return(future)
38
+ future.should_receive(:get)
39
+ client.close
40
+ end
41
+
42
+ it 'returns self' do
43
+ async_client.stub(:close).and_return(future)
44
+ client.close.should equal(client)
45
+ end
46
+ end
47
+
48
+ describe '#connected?' do
49
+ it 'delegates to the async client' do
50
+ async_client.stub(:connected?).and_return(true)
51
+ client.connected?.should be_true
52
+ async_client.stub(:connected?).and_return(false)
53
+ client.connected?.should be_false
54
+ end
55
+ end
56
+
57
+ describe '#keyspace' do
58
+ it 'delegates to the async client' do
59
+ async_client.stub(:keyspace).and_return('foo')
60
+ client.keyspace.should == 'foo'
61
+ end
62
+ end
63
+
64
+ describe '#use' do
65
+ it 'calls #use on the async client and waits for the result' do
66
+ async_client.should_receive(:use).with('foo').and_return(future)
67
+ future.should_receive(:get)
68
+ client.use('foo')
69
+ end
70
+ end
71
+
72
+ describe '#execute' do
73
+ it 'calls #execute on the async client and waits for, and returns the result' do
74
+ result = stub(:result)
75
+ async_client.stub(:execute).with('SELECT * FROM something', :one).and_return(future)
76
+ future.stub(:get).and_return(result)
77
+ client.execute('SELECT * FROM something', :one).should equal(result)
78
+ end
79
+ end
80
+
81
+ describe '#prepare' do
82
+ it 'calls #prepare on the async client, waits for the result and returns a SynchronousFuture' do
83
+ result = stub(:result)
84
+ metadata = stub(:metadata)
85
+ async_statement = stub(:async_statement, metadata: metadata)
86
+ another_future = stub(:another_future)
87
+ async_client.stub(:prepare).with('SELECT * FROM something').and_return(future)
88
+ future.stub(:get).and_return(async_statement)
89
+ statement = client.prepare('SELECT * FROM something')
90
+ async_statement.should_receive(:execute).and_return(another_future)
91
+ another_future.stub(:get).and_return(result)
92
+ statement.execute.should equal(result)
93
+ statement.metadata.should equal(metadata)
94
+ end
95
+ end
96
+
97
+ describe '#async' do
98
+ it 'returns an asynchronous client' do
99
+ client.async.should equal(async_client)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Client
8
+ describe SynchronousPreparedStatement do
9
+ let :statement do
10
+ described_class.new(async_statement)
11
+ end
12
+
13
+ let :async_statement do
14
+ stub(:async_statement, metadata: metadata)
15
+ end
16
+
17
+ let :metadata do
18
+ stub(:metadata)
19
+ end
20
+
21
+ let :future do
22
+ Future.new
23
+ end
24
+
25
+ describe '#metadata' do
26
+ it 'returns the async statement\'s metadata' do
27
+ statement.metadata.should equal(async_statement.metadata)
28
+ end
29
+ end
30
+
31
+ describe '#execute' do
32
+ it 'it calls #execute on the async statement and waits for the result' do
33
+ result = stub(:result)
34
+ async_statement.should_receive(:execute).with('one', 'two', :three).and_return(future)
35
+ future.complete!(result)
36
+ statement.execute('one', 'two', :three).should equal(result)
37
+ end
38
+ end
39
+
40
+ describe '#pipeline' do
41
+ it 'executes the statement multiple times and waits for all the results' do
42
+ result1 = stub(:result1)
43
+ result2 = stub(:result2)
44
+ async_statement.stub(:execute).with('one', 'two', :three).and_return(Future.completed(result1))
45
+ async_statement.stub(:execute).with('four', 'file', :all).and_return(Future.completed(result2))
46
+ results = statement.pipeline do |p|
47
+ p.execute('one', 'two', :three)
48
+ p.execute('four', 'file', :all)
49
+ end
50
+ results.should eql([result1, result2])
51
+ end
52
+
53
+ it 'does nothing when statements are executed' do
54
+ statement.pipeline { |p| }.should == []
55
+ end
56
+ end
57
+
58
+ describe '#async' do
59
+ it 'returns an asynchronous statement' do
60
+ statement.async.should equal(async_statement)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -190,6 +190,10 @@ module Cql
190
190
  f = Future.combine(Future.new, Future.new)
191
191
  expect { f.fail!(StandardError.new('Blurgh')) }.to raise_error(FutureError)
192
192
  end
193
+
194
+ it 'completes with an empty list when no futures are given' do
195
+ Future.combine.get.should == []
196
+ end
193
197
  end
194
198
  end
195
199
 
@@ -213,37 +213,56 @@ module Cql
213
213
  expect { f.get }.to raise_error(ConnectionNotFoundError)
214
214
  end
215
215
 
216
- it 'fails if the connection is busy' do
217
- f = io_reactor.start.flat_map do
218
- io_reactor.add_connection(host, port).flat_map do
219
- io_reactor.add_connection(host, port).flat_map do |connection_id|
220
- 200.times do
221
- io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
222
- end
223
- io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
224
- end
225
- end
216
+ it 'queues requests when the connection is busy' do
217
+ request = Cql::Protocol::QueryRequest.new('UPDATE x SET y = 1 WHERE z = 2', :one)
218
+
219
+ io_reactor.start
220
+ connection_id = io_reactor.add_connection(host, port).get
221
+
222
+ futures = 200.times.map do
223
+ io_reactor.queue_request(request, connection_id)
224
+ end
225
+
226
+ 128.times do |i|
227
+ server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
228
+ end
229
+
230
+ Future.combine(*futures.shift(128)).get
231
+
232
+ 128.times do |i|
233
+ server.broadcast!("\x81\x00#{[i].pack('c')}\b\x00\x00\x00\x04\x00\x00\x00\x01")
226
234
  end
227
- expect { f.get }.to raise_error(ConnectionBusyError)
235
+
236
+ Future.combine(*futures).get
228
237
  end
229
238
 
230
- it 'fails if the connection is busy, when there is only one connection' do
231
- f = io_reactor.start.flat_map do
232
- io_reactor.add_connection(host, port).flat_map do |connection_id|
233
- 200.times do
234
- io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
235
- end
236
- io_reactor.queue_request(Cql::Protocol::OptionsRequest.new, connection_id)
239
+ it 'fails queued requests when the connection closes' do
240
+ request = Cql::Protocol::QueryRequest.new('UPDATE x SET y = 1 WHERE z = 2', :one)
241
+
242
+ io_reactor.start
243
+ connection_id = io_reactor.add_connection(host, port).get
244
+
245
+ failures = 0
246
+
247
+ 200.times do
248
+ f = io_reactor.queue_request(request, connection_id)
249
+ f.on_failure do
250
+ failures += 1
237
251
  end
238
252
  end
239
- expect { f.get }.to raise_error(ConnectionBusyError)
253
+
254
+ server.broadcast!("\x01\x00\x00\x02\x00\x00\x00\x16")
255
+
256
+ await { failures == 200 }
240
257
  end
241
258
  end
242
259
 
243
260
  it 'fails if there is an error when encoding the request' do
261
+ request = stub(:request)
262
+ request.stub(:encode_frame).and_raise(Cql::ProtocolError.new('Boork!'))
244
263
  io_reactor.start
245
264
  io_reactor.add_connection(host, port).get
246
- f = io_reactor.queue_request(Cql::Protocol::QueryRequest.new('USE test', :foobar))
265
+ f = io_reactor.queue_request(request)
247
266
  expect { f.get }.to raise_error(Cql::ProtocolError)
248
267
  end
249
268
 
@@ -0,0 +1,17 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Protocol
8
+ describe Request do
9
+ describe '#encode_frame' do
10
+ it 'returns a rendered request frame for the specified channel' do
11
+ frame = PrepareRequest.new('SELECT * FROM things').encode_frame(3)
12
+ frame.to_s.should == "\x01\x00\x03\x09\x00\x00\x00\x18\x00\x00\x00\x14SELECT * FROM things"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Protocol
8
+ describe CredentialsRequest do
9
+ describe '#encode_frame' do
10
+ it 'encodes a CREDENTIALS request frame' do
11
+ bytes = CredentialsRequest.new('username' => 'cassandra', 'password' => 'ardnassac').encode_frame(3)
12
+ bytes.should == (
13
+ "\x01\x00\x03\04" +
14
+ "\x00\x00\x00\x2c" +
15
+ "\x00\x02" +
16
+ "\x00\x08username" +
17
+ "\x00\x09cassandra" +
18
+ "\x00\x08password" +
19
+ "\x00\x09ardnassac"
20
+ )
21
+ end
22
+ end
23
+
24
+ describe '#to_s' do
25
+ it 'returns a pretty string' do
26
+ request = CredentialsRequest.new('foo' => 'bar', 'hello' => 'world')
27
+ request.to_s.should == 'CREDENTIALS {"foo"=>"bar", "hello"=>"world"}'
28
+ end
29
+ end
30
+
31
+ describe '#eql?' do
32
+ it 'returns when the credentials are the same' do
33
+ c1 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
34
+ c2 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
35
+ c2.should eql(c2)
36
+ end
37
+
38
+ it 'returns when the credentials are equivalent' do
39
+ pending 'this would be nice, but is hardly necessary' do
40
+ c1 = CredentialsRequest.new(:username => 'foo', :password => 'bar')
41
+ c2 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
42
+ c1.should eql(c2)
43
+ end
44
+ end
45
+
46
+ it 'returns false when the credentials are different' do
47
+ c1 = CredentialsRequest.new('username' => 'foo', 'password' => 'world')
48
+ c2 = CredentialsRequest.new('username' => 'foo', 'hello' => 'world')
49
+ c1.should_not eql(c2)
50
+ end
51
+
52
+ it 'is aliased as ==' do
53
+ c1 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
54
+ c2 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
55
+ c1.should == c2
56
+ end
57
+ end
58
+
59
+ describe '#hash' do
60
+ it 'has the same hash code as another identical object' do
61
+ c1 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
62
+ c2 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
63
+ c1.hash.should == c2.hash
64
+ end
65
+
66
+ it 'has the same hash code as another object with equivalent credentials' do
67
+ pending 'this would be nice, but is hardly necessary' do
68
+ c1 = CredentialsRequest.new(:username => 'foo', :password => 'bar')
69
+ c2 = CredentialsRequest.new('username' => 'foo', 'password' => 'bar')
70
+ c1.hash.should == c2.hash
71
+ end
72
+ end
73
+
74
+ it 'does not have the same hash code when the credentials are different' do
75
+ c1 = CredentialsRequest.new('username' => 'foo', 'password' => 'world')
76
+ c2 = CredentialsRequest.new('username' => 'foo', 'hello' => 'world')
77
+ c1.hash.should_not == c2.hash
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,174 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Cql
7
+ module Protocol
8
+ describe ExecuteRequest do
9
+ let :id do
10
+ "\xCAH\x7F\x1Ez\x82\xD2<N\x8A\xF35Qq\xA5/"
11
+ end
12
+
13
+ let :column_metadata do
14
+ [
15
+ ['ks', 'tbl', 'col1', :varchar],
16
+ ['ks', 'tbl', 'col2', :int],
17
+ ['ks', 'tbl', 'col3', :varchar]
18
+ ]
19
+ end
20
+
21
+ let :values do
22
+ ['hello', 42, 'foo']
23
+ end
24
+
25
+ describe '#initialize' do
26
+ it 'raises an error when the metadata and values don\'t have the same size' do
27
+ expect { ExecuteRequest.new(id, column_metadata, ['hello', 42], :each_quorum) }.to raise_error(ArgumentError)
28
+ end
29
+
30
+ it 'raises an error for unsupported column types' do
31
+ column_metadata[2][3] = :imaginary
32
+ expect { ExecuteRequest.new(id, column_metadata, ['hello', 42, 'foo'], :each_quorum) }.to raise_error(UnsupportedColumnTypeError)
33
+ end
34
+
35
+ it 'raises an error for unsupported column collection types' do
36
+ column_metadata[2][3] = [:imaginary, :varchar]
37
+ expect { ExecuteRequest.new(id, column_metadata, ['hello', 42, ['foo']], :each_quorum) }.to raise_error(UnsupportedColumnTypeError)
38
+ end
39
+
40
+ it 'raises an error when collection values are not enumerable' do
41
+ column_metadata[2][3] = [:set, :varchar]
42
+ expect { ExecuteRequest.new(id, column_metadata, ['hello', 42, 'foo'], :each_quorum) }.to raise_error(InvalidValueError)
43
+ end
44
+
45
+ it 'raises an error when it cannot encode the argument' do
46
+ expect { ExecuteRequest.new(id, column_metadata, ['hello', 'not an int', 'foo'], :each_quorum) }.to raise_error(TypeError, /cannot be encoded as INT/)
47
+ end
48
+ end
49
+
50
+ describe '#encode_frame' do
51
+ it 'encodes an EXECUTE request frame' do
52
+ bytes = ExecuteRequest.new(id, column_metadata, ['hello', 42, 'foo'], :each_quorum).encode_frame(3)
53
+ bytes.should == "\x01\x00\x03\x0a\x00\x00\x00\x2e\x00\x10\xCAH\x7F\x1Ez\x82\xD2<N\x8A\xF35Qq\xA5/\x00\x03\x00\x00\x00\x05hello\x00\x00\x00\x04\x00\x00\x00\x2a\x00\x00\x00\x03foo\x00\x07"
54
+ end
55
+
56
+ specs = [
57
+ [:ascii, 'test', "test"],
58
+ [:bigint, 1012312312414123, "\x00\x03\x98\xB1S\xC8\x7F\xAB"],
59
+ [:blob, "\xab\xcd", "\xab\xcd"],
60
+ [:boolean, false, "\x00"],
61
+ [:boolean, true, "\x01"],
62
+ [:decimal, BigDecimal.new('1042342234234.123423435647768234'), "\x00\x00\x00\x12\r'\xFDI\xAD\x80f\x11g\xDCfV\xAA"],
63
+ [:double, 10000.123123123, "@\xC3\x88\x0F\xC2\x7F\x9DU"],
64
+ [:float, 12.13, "AB\x14{"],
65
+ [:inet, IPAddr.new('8.8.8.8'), "\x08\x08\x08\x08"],
66
+ [:inet, IPAddr.new('::1'), "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"],
67
+ [:int, 12348098, "\x00\xBCj\xC2"],
68
+ [:text, 'FOOBAR', 'FOOBAR'],
69
+ [:timestamp, Time.at(1358013521.123), "\x00\x00\x01</\xE9\xDC\xE3"],
70
+ [:timeuuid, Uuid.new('a4a70900-24e1-11df-8924-001ff3591711'), "\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11"],
71
+ [:uuid, Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6'), "\xCF\xD6l\xCC\xD8WN\x90\xB1\xE5\xDF\x98\xA3\xD4\f\xD6"],
72
+ [:varchar, 'hello', 'hello'],
73
+ [:varint, 1231312312331283012830129382342342412123, "\x03\x9EV \x15\f\x03\x9DK\x18\xCDI\\$?\a["],
74
+ [:varint, -234234234234, "\xC9v\x8D:\x86"],
75
+ [[:list, :timestamp], [Time.at(1358013521.123)], "\x00\x01" + "\x00\x08\x00\x00\x01</\xE9\xDC\xE3"],
76
+ [[:list, :boolean], [true, false, true, true], "\x00\x04" + "\x00\x01\x01" + "\x00\x01\x00" + "\x00\x01\x01" + "\x00\x01\x01"],
77
+ [[:map, :uuid, :int], {Uuid.new('cfd66ccc-d857-4e90-b1e5-df98a3d40cd6') => 45345, Uuid.new('a4a70900-24e1-11df-8924-001ff3591711') => 98765}, "\x00\x02" + "\x00\x10\xCF\xD6l\xCC\xD8WN\x90\xB1\xE5\xDF\x98\xA3\xD4\f\xD6" + "\x00\x04\x00\x00\xb1\x21" + "\x00\x10\xA4\xA7\t\x00$\xE1\x11\xDF\x89$\x00\x1F\xF3Y\x17\x11" + "\x00\x04\x00\x01\x81\xcd"],
78
+ [[:map, :ascii, :blob], {'hello' => 'world', 'one' => "\x01", 'two' => "\x02"}, "\x00\x03" + "\x00\x05hello" + "\x00\x05world" + "\x00\x03one" + "\x00\x01\x01" + "\x00\x03two" + "\x00\x01\x02"],
79
+ [[:set, :int], Set.new([13, 3453, 456456, 123, 768678]), "\x00\x05" + "\x00\x04\x00\x00\x00\x0d" + "\x00\x04\x00\x00\x0d\x7d" + "\x00\x04\x00\x06\xf7\x08" + "\x00\x04\x00\x00\x00\x7b" + "\x00\x04\x00\x0b\xba\xa6"],
80
+ [[:set, :varchar], Set.new(['foo', 'bar', 'baz']), "\x00\x03" + "\x00\x03foo" + "\x00\x03bar" + "\x00\x03baz"],
81
+ [[:set, :int], [13, 3453, 456456, 123, 768678], "\x00\x05" + "\x00\x04\x00\x00\x00\x0d" + "\x00\x04\x00\x00\x0d\x7d" + "\x00\x04\x00\x06\xf7\x08" + "\x00\x04\x00\x00\x00\x7b" + "\x00\x04\x00\x0b\xba\xa6"],
82
+ [[:set, :varchar], ['foo', 'bar', 'baz'], "\x00\x03" + "\x00\x03foo" + "\x00\x03bar" + "\x00\x03baz"]
83
+ ]
84
+ specs.each do |type, value, expected_bytes|
85
+ it "encodes #{type} values" do
86
+ metadata = [['ks', 'tbl', 'id_column', type]]
87
+ buffer = ExecuteRequest.new(id, metadata, [value], :one).encode_frame(3)
88
+ buffer.discard(8 + 2 + 16 + 2)
89
+ length = buffer.read_int
90
+ result_bytes = buffer.read(length)
91
+ result_bytes.should eql_bytes(expected_bytes)
92
+ end
93
+ end
94
+ end
95
+
96
+ describe '#to_s' do
97
+ it 'returns a pretty string' do
98
+ request = ExecuteRequest.new(id, column_metadata, values, :each_quorum)
99
+ request.to_s.should == 'EXECUTE ca487f1e7a82d23c4e8af3355171a52f ["hello", 42, "foo"] EACH_QUORUM'
100
+ end
101
+ end
102
+
103
+ describe '#eql?' do
104
+ it 'returns true when the ID, metadata, values and consistency are the same' do
105
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
106
+ e2 = ExecuteRequest.new(id, column_metadata, values, :one)
107
+ e1.should eql(e2)
108
+ end
109
+
110
+ it 'returns false when the ID is different' do
111
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
112
+ e2 = ExecuteRequest.new(id.reverse, column_metadata, values, :one)
113
+ e1.should_not eql(e2)
114
+ end
115
+
116
+ it 'returns false when the metadata is different' do
117
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
118
+ e2 = ExecuteRequest.new(id, column_metadata.reverse, values, :one)
119
+ e1.should_not eql(e2)
120
+ end
121
+
122
+ it 'returns false when the values are different' do
123
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
124
+ e2 = ExecuteRequest.new(id, column_metadata, values.reverse, :one)
125
+ e1.should_not eql(e2)
126
+ end
127
+
128
+ it 'returns false when the consistency is different' do
129
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
130
+ e2 = ExecuteRequest.new(id, column_metadata, values, :two)
131
+ e1.should_not eql(e2)
132
+ end
133
+
134
+ it 'is aliased as ==' do
135
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
136
+ e2 = ExecuteRequest.new(id, column_metadata, values, :one)
137
+ e1.should == e2
138
+ end
139
+ end
140
+
141
+ describe '#hash' do
142
+ it 'has the same hash code as another identical object' do
143
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
144
+ e2 = ExecuteRequest.new(id, column_metadata, values, :one)
145
+ e1.hash.should == e2.hash
146
+ end
147
+
148
+ it 'does not have the same hash code when the ID is different' do
149
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
150
+ e2 = ExecuteRequest.new(id.reverse, column_metadata, values, :one)
151
+ e1.hash.should_not == e2.hash
152
+ end
153
+
154
+ it 'does not have the same hash code when the metadata is different' do
155
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
156
+ e2 = ExecuteRequest.new(id, column_metadata.reverse, values, :one)
157
+ e1.hash.should_not == e2.hash
158
+ end
159
+
160
+ it 'does not have the same hash code when the values are different' do
161
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
162
+ e2 = ExecuteRequest.new(id, column_metadata, values.reverse, :one)
163
+ e1.hash.should_not == e2.hash
164
+ end
165
+
166
+ it 'does not have the same hash code when the consistency is different' do
167
+ e1 = ExecuteRequest.new(id, column_metadata, values, :one)
168
+ e2 = ExecuteRequest.new(id, column_metadata, values, :two)
169
+ e1.hash.should_not == e2.hash
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end