jamesgolick-cassandra 0.8.2

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.
@@ -0,0 +1,141 @@
1
+ # OrderedHash is namespaced to prevent conflicts with other implementations
2
+ class Cassandra
3
+ # Hash is ordered in Ruby 1.9!
4
+ if RUBY_VERSION >= '1.9'
5
+ OrderedHash = ::Hash
6
+ else
7
+ class OrderedHash < Hash #:nodoc:
8
+ def initialize(*args, &block)
9
+ super
10
+ @keys = []
11
+ end
12
+
13
+ def self.[](*args)
14
+ ordered_hash = new
15
+
16
+ if (args.length == 1 && args.first.is_a?(Array))
17
+ args.first.each do |key_value_pair|
18
+ next unless (key_value_pair.is_a?(Array))
19
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
20
+ end
21
+
22
+ return ordered_hash
23
+ end
24
+
25
+ unless (args.size % 2 == 0)
26
+ raise ArgumentError.new("odd number of arguments for Hash")
27
+ end
28
+
29
+ args.each_with_index do |val, ind|
30
+ next if (ind % 2 != 0)
31
+ ordered_hash[val] = args[ind + 1]
32
+ end
33
+
34
+ ordered_hash
35
+ end
36
+
37
+ def initialize_copy(other)
38
+ super
39
+ # make a deep copy of keys
40
+ @keys = other.keys
41
+ end
42
+
43
+ def []=(key, value)
44
+ @keys << key if !has_key?(key)
45
+ super
46
+ end
47
+
48
+ def delete(key)
49
+ if has_key? key
50
+ index = @keys.index(key)
51
+ @keys.delete_at index
52
+ end
53
+ super
54
+ end
55
+
56
+ def delete_if
57
+ super
58
+ sync_keys!
59
+ self
60
+ end
61
+
62
+ def reject!
63
+ super
64
+ sync_keys!
65
+ self
66
+ end
67
+
68
+ def reject(&block)
69
+ dup.reject!(&block)
70
+ end
71
+
72
+ def keys
73
+ @keys.dup
74
+ end
75
+
76
+ def values
77
+ @keys.collect { |key| self[key] }
78
+ end
79
+
80
+ def to_hash
81
+ self
82
+ end
83
+
84
+ def to_a
85
+ @keys.map { |key| [ key, self[key] ] }
86
+ end
87
+
88
+ def each_key
89
+ @keys.each { |key| yield key }
90
+ end
91
+
92
+ def each_value
93
+ @keys.each { |key| yield self[key]}
94
+ end
95
+
96
+ def each
97
+ @keys.each {|key| yield [key, self[key]]}
98
+ end
99
+
100
+ alias_method :each_pair, :each
101
+
102
+ def clear
103
+ super
104
+ @keys.clear
105
+ self
106
+ end
107
+
108
+ def shift
109
+ k = @keys.first
110
+ v = delete(k)
111
+ [k, v]
112
+ end
113
+
114
+ def merge!(other_hash)
115
+ other_hash.each {|k,v| self[k] = v }
116
+ self
117
+ end
118
+
119
+ def merge(other_hash)
120
+ dup.merge!(other_hash)
121
+ end
122
+
123
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
124
+ def replace(other)
125
+ super
126
+ @keys = other.keys
127
+ self
128
+ end
129
+
130
+ def inspect
131
+ "#<OrderedHash #{super}>"
132
+ end
133
+
134
+ private
135
+
136
+ def sync_keys!
137
+ @keys.delete_if {|k| !has_key?(k)}
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,92 @@
1
+
2
+ class Cassandra
3
+ # Inner methods for actually doing the Thrift calls
4
+ module Protocol #:nodoc:
5
+ private
6
+
7
+ def _mutate(mutation_map, consistency_level)
8
+ client.batch_mutate(@keyspace, mutation_map, consistency_level)
9
+ end
10
+
11
+ def _remove(key, column_path, timestamp, consistency_level)
12
+ client.remove(@keyspace, key, column_path, timestamp, consistency_level)
13
+ end
14
+
15
+ def _count_columns(column_family, key, super_column, consistency)
16
+ client.get_count(@keyspace, key,
17
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
18
+ consistency
19
+ )
20
+ end
21
+
22
+ def _get_columns(column_family, key, columns, sub_columns, consistency)
23
+ result = if is_super(column_family)
24
+ if sub_columns
25
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
26
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
27
+ CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
28
+ consistency))
29
+ else
30
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
31
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
32
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
33
+ consistency))
34
+ end
35
+ else
36
+ columns_to_hash(column_family, client.get_slice(@keyspace, key,
37
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
38
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
39
+ consistency))
40
+ end
41
+
42
+ klass = column_name_class(column_family)
43
+ (sub_columns || columns).map { |name| result[klass.new(name)] }
44
+ end
45
+
46
+ def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
47
+ # Single values; count and range parameters have no effect
48
+ if is_super(column_family) and sub_column
49
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
50
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
51
+ elsif !is_super(column_family) and column
52
+ column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
53
+ multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
54
+
55
+ # Slices
56
+ else
57
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
58
+ CassandraThrift::SliceRange.new(
59
+ :reversed => reversed,
60
+ :count => count,
61
+ :start => start,
62
+ :finish => finish))
63
+
64
+ if is_super(column_family) and column
65
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
66
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
67
+ else
68
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
69
+ multi_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
70
+ end
71
+ end
72
+ end
73
+
74
+ def _get_range(column_family, start, finish, count, consistency)
75
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
76
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range => CassandraThrift::SliceRange.new(:start => '', :finish => ''))
77
+ range = CassandraThrift::KeyRange.new(:start_key => start, :end_key => finish, :count => count)
78
+ client.get_range_slices(@keyspace, column_parent, predicate, range, 1)
79
+ end
80
+
81
+ def _get_range_keys(column_family, start, finish, count, consistency)
82
+ _get_range(column_family, start, finish, count, consistency).collect{|i| i.key }
83
+ end
84
+
85
+ def each_key(column_family)
86
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family.to_s)
87
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [])
88
+ range = CassandraThrift::KeyRange.new(:start_key => '', :end_key => '')
89
+ client.get_range_slices(@keyspace, column_parent, predicate, range, 1).each{|i| yield i.key }
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class Time
3
+ def self.stamp
4
+ Time.now.stamp
5
+ end
6
+
7
+ def stamp
8
+ to_i * 1_000_000 + usec
9
+ end
10
+ end
11
+
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class CassandraClientTest < Test::Unit::TestCase
4
+ include Cassandra::Constants
5
+
6
+ def setup
7
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
8
+ end
9
+
10
+ def test_client_method_is_called
11
+ assert_nil @twitter.instance_variable_get(:@client)
12
+ @twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
13
+ assert_not_nil @twitter.instance_variable_get(:@client)
14
+ end
15
+
16
+ def key
17
+ caller.first[/`(.*?)'/, 1]
18
+ end
19
+
20
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+ require 'cassandra_test'
3
+ require 'cassandra/mock'
4
+
5
+ class CassandraMockTest < CassandraTest
6
+ include Cassandra::Constants
7
+
8
+ def setup
9
+ storage_xml_path = File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'conf', 'storage-conf.xml'))
10
+ @twitter = Cassandra::Mock.new('Twitter', storage_xml_path)
11
+ @twitter.clear_keyspace!
12
+
13
+ @blogs = Cassandra::Mock.new('Multiblog', storage_xml_path)
14
+ @blogs.clear_keyspace!
15
+
16
+ @blogs_long = Cassandra::Mock.new('MultiblogLong', storage_xml_path)
17
+ @blogs_long.clear_keyspace!
18
+
19
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
20
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
21
+ end
22
+
23
+ def test_setup
24
+ assert @twitter
25
+ assert @blogs
26
+ assert @blogs_long
27
+ end
28
+
29
+ def test_schema_for_keyspace
30
+ data = {
31
+ "StatusRelationships"=>{
32
+ "CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
33
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
34
+ "Type"=>"Super"},
35
+ "StatusAudits"=>{
36
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
37
+ "Type"=>"Standard"},
38
+ "Statuses"=>{
39
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
40
+ "Type"=>"Standard"},
41
+ "UserRelationships"=>{
42
+ "CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
43
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
44
+ "Type"=>"Super"},
45
+ "UserAudits"=>{
46
+ "CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
47
+ "Type"=>"Standard"},
48
+ "Users"=>{"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type", "Type"=>"Standard"},
49
+ "TimelinishThings"=>
50
+ {"CompareWith"=>"org.apache.cassandra.db.marshal.BytesType", "Type"=>"Standard"}
51
+ }
52
+ stuff = @twitter.send(:schema_for_keyspace, 'Twitter')
53
+ data.keys.each do |k|
54
+ assert_equal data[k], stuff[k], k
55
+ end
56
+ end
57
+
58
+ def test_sorting_row_keys
59
+ @twitter.insert(:Statuses, 'b', {:text => 'foo'})
60
+ @twitter.insert(:Statuses, 'a', {:text => 'foo'})
61
+ assert_equal ['a'], @twitter.get_range(:Statuses, :count => 1)
62
+ end
63
+
64
+ def test_inserting_array_for_indices
65
+ @twitter.insert(:TimelinishThings, 'a', ['1','2'])
66
+ row = @twitter.get(:TimelinishThings, 'a')
67
+ assert_equal({'1' => nil, '2' => nil}, row)
68
+
69
+ assert_raises(ArgumentError) {
70
+ @twitter.insert(:UserRelationships, 'a', ['u1','u2'])
71
+ }
72
+ end
73
+ end
@@ -0,0 +1,370 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class CassandraTest < Test::Unit::TestCase
4
+ include Cassandra::Constants
5
+
6
+ def setup
7
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
8
+ @twitter.clear_keyspace!
9
+
10
+ @blogs = Cassandra.new('Multiblog')
11
+ @blogs.clear_keyspace!
12
+
13
+ @blogs_long = Cassandra.new('MultiblogLong')
14
+ @blogs_long.clear_keyspace!
15
+
16
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
17
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
18
+ end
19
+
20
+ def test_inspect
21
+ assert_nothing_raised do
22
+ @blogs.inspect
23
+ @twitter.inspect
24
+ end
25
+ end
26
+
27
+ def test_get_key
28
+ @twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
29
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
30
+ assert_equal({}, @twitter.get(:Users, 'bogus'))
31
+ end
32
+
33
+ def test_get_key_preserving_order
34
+ # In-order hash is preserved
35
+ hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
36
+ @twitter.insert(:Users, key, hash)
37
+ assert_equal(hash.keys, @twitter.get(:Users, key).keys)
38
+
39
+ @twitter.remove(:Users, key)
40
+
41
+ # Out-of-order hash is returned sorted
42
+ hash = OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
43
+ @twitter.insert(:Users, key, hash)
44
+ assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
45
+ assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
46
+ end
47
+
48
+ def test_get_first_time_uuid_column
49
+ @blogs.insert(:Blogs, key,
50
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
51
+
52
+ assert_equal({@uuids[0] => 'I like this cat'}, @blogs.get(:Blogs, key, :count => 1))
53
+ assert_equal({@uuids[2] => 'I disagree'}, @blogs.get(:Blogs, key, :count => 1, :reversed => true))
54
+ assert_equal({}, @blogs.get(:Blogs, 'bogus'))
55
+ end
56
+
57
+ def test_get_multiple_time_uuid_columns
58
+ @blogs.insert(:Blogs, key,
59
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
60
+
61
+ assert_equal(['I like this cat', 'Buttons is cuter'], @blogs.get_columns(:Blogs, key, @uuids[0..1]))
62
+ end
63
+
64
+ def test_get_first_long_column
65
+ @blogs_long.insert(:Blogs, key,
66
+ {@longs[0] => 'I like this cat', @longs[1] => 'Buttons is cuter', @longs[2] => 'I disagree'})
67
+
68
+ assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
69
+ assert_equal({@longs[2] => 'I disagree'}, @blogs_long.get(:Blogs, key, :count => 1, :reversed => true))
70
+ assert_equal({}, @blogs_long.get(:Blogs, 'bogus'))
71
+ end
72
+
73
+ def test_long_remove_bug
74
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
75
+ @blogs_long.remove(:Blogs, key)
76
+ assert_equal({}, @blogs_long.get(:Blogs, key, :count => 1))
77
+
78
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I really like this cat'})
79
+ assert_equal({@longs[0] => 'I really like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
80
+ end
81
+
82
+ def test_get_with_count
83
+ @twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
84
+ assert_equal 1, @twitter.get(:Statuses, key, :count => 1).size
85
+ assert_equal 2, @twitter.get(:Statuses, key, :count => 2).size
86
+ end
87
+
88
+ def test_get_value
89
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
90
+ assert_equal 'v', @twitter.get(:Statuses, key, 'body')
91
+ assert_nil @twitter.get(:Statuses, 'bogus', 'body')
92
+
93
+ assert @twitter.exists?(:Statuses, key, 'body')
94
+ assert !@twitter.exists?(:Statuses, 'bogus', 'body')
95
+ end
96
+
97
+ def test_exists_with_only_key
98
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
99
+ assert @twitter.exists?(:Statuses, key)
100
+ end
101
+
102
+ def test_get_super_key
103
+ columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
104
+ @twitter.insert(:StatusRelationships, key, columns)
105
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
106
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
107
+ end
108
+
109
+ def test_get_several_super_keys
110
+ columns = {
111
+ 'user_timelines' => {@uuids[1] => 'v1'},
112
+ 'mentions_timelines' => {@uuids[2] => 'v2'}}
113
+ @twitter.insert(:StatusRelationships, key, columns)
114
+
115
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
116
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
117
+ end
118
+
119
+ def test_get_super_sub_keys_with_count
120
+ @twitter.insert(:StatusRelationships, key,
121
+ {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2', @uuids[3] => 'v3'}})
122
+ assert_equal({@uuids[1] => 'v1'},
123
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1))
124
+ assert_equal({@uuids[3] => 'v3'},
125
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true))
126
+ end
127
+
128
+ def test_get_super_sub_keys_with_ranges
129
+ @twitter.insert(:StatusRelationships, key,
130
+ {'user_timelines' => {
131
+ @uuids[1] => 'v1',
132
+ @uuids[2] => 'v2',
133
+ @uuids[3] => 'v3',
134
+ @uuids[4] => 'v4',
135
+ @uuids[5] => 'v5'}})
136
+
137
+ keys = @twitter.get(:StatusRelationships, key, "user_timelines").keys
138
+ assert_equal keys.sort, keys
139
+ assert_equal({@uuids[1] => 'v1'}, @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1))
140
+ assert_equal({@uuids[2] => 'v2'}, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1))
141
+ assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).size
142
+ end
143
+
144
+ def test_get_super_sub_key
145
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
146
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
147
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
148
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
149
+ # FIXME Not sure if this is valid
150
+ # assert_nil @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
151
+ end
152
+
153
+ def test_get_super_value
154
+ columns = {@uuids[1] => 'v1'}
155
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
156
+ assert_equal('v1', @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first))
157
+ assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
158
+ end
159
+
160
+
161
+ #TODO: add a OPP keyspace for this
162
+ # def test_get_range
163
+ # @twitter.insert(:Statuses, '2', {'body' => '1'})
164
+ # @twitter.insert(:Statuses, '3', {'body' => '1'})
165
+ # @twitter.insert(:Statuses, '4', {'body' => '1'})
166
+ # @twitter.insert(:Statuses, '5', {'body' => '1'})
167
+ # @twitter.insert(:Statuses, '6', {'body' => '1'})
168
+ # assert_equal(['3', '4', '5'], @twitter.get_range(:Statuses, :start => '3', :finish => '5'))
169
+ # end
170
+
171
+ def test_get_range_count
172
+ @twitter.insert(:Statuses, '2', {'body' => '1'})
173
+ @twitter.insert(:Statuses, '3', {'body' => '1'})
174
+ @twitter.insert(:Statuses, '4', {'body' => '1'})
175
+ @twitter.insert(:Statuses, '5', {'body' => '1'})
176
+ @twitter.insert(:Statuses, '6', {'body' => '1'})
177
+ assert_equal(3, @twitter.get_range(:Statuses, :count => 3).size)
178
+ end
179
+
180
+ def test_multi_get
181
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
182
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
183
+
184
+ expected = OrderedHash[key + '1', {'body' => 'v1', 'user' => 'v1'}, key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}]
185
+ result = @twitter.multi_get(:Users, [key + '1', key + '2', 'bogus'])
186
+ assert_equal expected, result
187
+ assert_equal expected.keys, result.keys
188
+
189
+ expected = OrderedHash[key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}, key + '1', {'body' => 'v1', 'user' => 'v1'}]
190
+ result = @twitter.multi_get(:Users, [key + '2', 'bogus', key + '1'])
191
+ assert_equal expected, result
192
+ assert_equal expected.keys, result.keys
193
+ end
194
+
195
+ def test_remove_key
196
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
197
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
198
+
199
+ @twitter.remove(:Statuses, key)
200
+ assert_equal({}, @twitter.get(:Statuses, key))
201
+ end
202
+
203
+ def test_remove_value
204
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
205
+ @twitter.remove(:Statuses, key, 'body')
206
+ assert_nil @twitter.get(:Statuses, key, 'body')
207
+ end
208
+
209
+ def test_remove_super_key
210
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
211
+ @twitter.remove(:StatusRelationships, key)
212
+ assert_equal({}, @twitter.get(:StatusRelationships, key))
213
+ end
214
+
215
+ def test_remove_super_sub_key
216
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
217
+ @twitter.remove(:StatusRelationships, key, 'user_timelines')
218
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
219
+ end
220
+
221
+ def test_remove_super_value
222
+ columns = {@uuids[1] => 'v1'}
223
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
224
+ @twitter.remove(:StatusRelationships, key, 'user_timelines', columns.keys.first)
225
+ assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first)
226
+ end
227
+
228
+ def test_clear_column_family
229
+ @twitter.insert(:Statuses, key + "1", {'body' => '1'})
230
+ @twitter.insert(:Statuses, key + "2", {'body' => '2'})
231
+ @twitter.insert(:Statuses, key + "3", {'body' => '3'})
232
+ @twitter.clear_column_family!(:Statuses)
233
+ assert_equal 0, @twitter.count_range(:Statuses)
234
+ end
235
+
236
+ def test_insert_key
237
+ @twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
238
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
239
+ end
240
+
241
+ def test_insert_super_key
242
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
243
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
244
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
245
+ end
246
+
247
+ def test_get_columns
248
+ @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
249
+ assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key, ['body', 'user']))
250
+ end
251
+
252
+ def test_get_column_values_super
253
+ user_columns, mentions_columns = {@uuids[1] => 'v1'}, {@uuids[2] => 'v2'}
254
+ @twitter.insert(:StatusRelationships, key,
255
+ {'user_timelines' => user_columns, 'mentions_timelines' => mentions_columns})
256
+ assert_equal [user_columns, mentions_columns],
257
+ @twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
258
+ end
259
+
260
+ def test_get_sub_column_values_super
261
+ user_columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
262
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => user_columns})
263
+ assert_equal ['v1', 'v2'],
264
+ @twitter.get_columns(:StatusRelationships, key, 'user_timelines', @uuids[1..2])
265
+ end
266
+
267
+ def test_multi_get_columns
268
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
269
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
270
+ assert_equal(
271
+ OrderedHash[key + '1', ['v1', 'v1'], key + '2', ['v2', 'v2'], 'bogus', [nil, nil]],
272
+ @twitter.multi_get_columns(:Users, [key + '1', key + '2', 'bogus'], ['body', 'user']))
273
+ assert_equal(
274
+ OrderedHash[key + '2', ['v2', 'v2'], 'bogus', [nil, nil], key + '1', ['v1', 'v1']],
275
+ @twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']))
276
+ end
277
+
278
+ def test_count_keys
279
+ @twitter.insert(:Statuses, key + "1", {'body' => '1'})
280
+ @twitter.insert(:Statuses, key + "2", {'body' => '2'})
281
+ @twitter.insert(:Statuses, key + "3", {'body' => '3'})
282
+ assert_equal 3, @twitter.count_range(:Statuses)
283
+ end
284
+
285
+ def test_count_columns
286
+ @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
287
+ assert_equal 2, @twitter.count_columns(:Statuses, key)
288
+ end
289
+
290
+ def test_count_super_columns
291
+ @twitter.insert(:StatusRelationships, key, {
292
+ 'user_timelines' => {@uuids[1] => 'v1'},
293
+ 'mentions_timelines' => {@uuids[2] => 'v2'}})
294
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
295
+ end
296
+
297
+ def test_count_super_sub_columns
298
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'}})
299
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
300
+ end
301
+
302
+ def test_multi_count_columns
303
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
304
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
305
+ assert_equal(
306
+ OrderedHash[key + '1', 2, key + '2', 2, 'bogus', 0],
307
+ @twitter.multi_count_columns(:Users, [key + '1', key + '2', 'bogus']))
308
+ assert_equal(
309
+ OrderedHash[key + '2', 2, 'bogus', 0, key + '1', 2],
310
+ @twitter.multi_count_columns(:Users, [key + '2', 'bogus', key + '1']))
311
+ end
312
+
313
+ def test_batch_mutate
314
+ k = key
315
+
316
+ @twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
317
+
318
+ @twitter.batch do
319
+ @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
320
+ @twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
321
+ @twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
322
+ @twitter.insert(:Statuses, k + '3', {'body' => 'v'})
323
+
324
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
325
+ assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
326
+ assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
327
+
328
+ @twitter.remove(:Users, k + '1')
329
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
330
+
331
+ @twitter.remove(:Users, k + '4')
332
+ @twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
333
+ assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
334
+ end
335
+
336
+ assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
337
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}, @twitter.get(:Users, k + '3')) # Written and compacted
338
+ assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
339
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
340
+ assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
341
+ end
342
+
343
+ def test_complain_about_nil_key
344
+ assert_raises(ArgumentError) do
345
+ @twitter.insert(:Statuses, nil, {'text' => 'crap'})
346
+ end
347
+ end
348
+
349
+ def test_raise_access_error_on_nonexistent_keyspace
350
+ nonexistent = Cassandra.new('Nonexistent')
351
+ assert_raises(Cassandra::AccessError) do
352
+ nonexistent.get "foo", "bar"
353
+ end
354
+ end
355
+
356
+ def test_nil_sub_column_value
357
+ @twitter.insert(:Index, 'asdf', {"thing" => {'jkl' => nil} })
358
+ end
359
+
360
+ def test_disconnect!
361
+ @twitter.disconnect!
362
+ assert_nil @twitter.instance_variable_get(:@client)
363
+ end
364
+
365
+ private
366
+
367
+ def key
368
+ caller.first[/`(.*?)'/, 1]
369
+ end
370
+ end