sessionm-cassandra 1.0.0

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 (96) hide show
  1. data/CHANGELOG +135 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE +202 -0
  4. data/Manifest +94 -0
  5. data/README.md +373 -0
  6. data/Rakefile +195 -0
  7. data/bin/cassandra_helper +16 -0
  8. data/conf/0.6/cassandra.in.sh +47 -0
  9. data/conf/0.6/log4j.properties +38 -0
  10. data/conf/0.6/schema.json +57 -0
  11. data/conf/0.6/storage-conf.xml +352 -0
  12. data/conf/0.7/cassandra.in.sh +46 -0
  13. data/conf/0.7/cassandra.yaml +336 -0
  14. data/conf/0.7/log4j-server.properties +41 -0
  15. data/conf/0.7/schema.json +57 -0
  16. data/conf/0.7/schema.txt +45 -0
  17. data/conf/0.8/cassandra.in.sh +41 -0
  18. data/conf/0.8/cassandra.yaml +61 -0
  19. data/conf/0.8/log4j-server.properties +40 -0
  20. data/conf/0.8/schema.json +72 -0
  21. data/conf/0.8/schema.txt +57 -0
  22. data/conf/1.0/cassandra.in.sh +41 -0
  23. data/conf/1.0/cassandra.yaml +415 -0
  24. data/conf/1.0/log4j-server.properties +40 -0
  25. data/conf/1.0/schema.json +72 -0
  26. data/conf/1.0/schema.txt +57 -0
  27. data/conf/1.1/cassandra.in.sh +41 -0
  28. data/conf/1.1/cassandra.yaml +567 -0
  29. data/conf/1.1/log4j-server.properties +44 -0
  30. data/conf/1.1/schema.json +72 -0
  31. data/conf/1.1/schema.txt +57 -0
  32. data/ext/cassandra_native.c +34 -0
  33. data/ext/extconf.rb +9 -0
  34. data/lib/cassandra/0.6/cassandra.rb +113 -0
  35. data/lib/cassandra/0.6/columns.rb +78 -0
  36. data/lib/cassandra/0.6/protocol.rb +91 -0
  37. data/lib/cassandra/0.6.rb +7 -0
  38. data/lib/cassandra/0.7/cassandra.rb +2 -0
  39. data/lib/cassandra/0.7/columns.rb +4 -0
  40. data/lib/cassandra/0.7/protocol.rb +5 -0
  41. data/lib/cassandra/0.7.rb +7 -0
  42. data/lib/cassandra/0.8/cassandra.rb +51 -0
  43. data/lib/cassandra/0.8/columns.rb +28 -0
  44. data/lib/cassandra/0.8/protocol.rb +10 -0
  45. data/lib/cassandra/0.8.rb +7 -0
  46. data/lib/cassandra/1.0/cassandra.rb +1 -0
  47. data/lib/cassandra/1.0/columns.rb +1 -0
  48. data/lib/cassandra/1.0/protocol.rb +1 -0
  49. data/lib/cassandra/1.0.rb +7 -0
  50. data/lib/cassandra/1.1/cassandra.rb +1 -0
  51. data/lib/cassandra/1.1/columns.rb +1 -0
  52. data/lib/cassandra/1.1/protocol.rb +1 -0
  53. data/lib/cassandra/1.1.rb +7 -0
  54. data/lib/cassandra/array.rb +8 -0
  55. data/lib/cassandra/batch.rb +41 -0
  56. data/lib/cassandra/cassandra.rb +1088 -0
  57. data/lib/cassandra/column_family.rb +3 -0
  58. data/lib/cassandra/columns.rb +172 -0
  59. data/lib/cassandra/comparable.rb +28 -0
  60. data/lib/cassandra/composite.rb +140 -0
  61. data/lib/cassandra/constants.rb +11 -0
  62. data/lib/cassandra/debug.rb +9 -0
  63. data/lib/cassandra/dynamic_composite.rb +96 -0
  64. data/lib/cassandra/helpers.rb +41 -0
  65. data/lib/cassandra/keyspace.rb +3 -0
  66. data/lib/cassandra/long.rb +58 -0
  67. data/lib/cassandra/mock.rb +525 -0
  68. data/lib/cassandra/ordered_hash.rb +192 -0
  69. data/lib/cassandra/protocol.rb +137 -0
  70. data/lib/cassandra/time.rb +11 -0
  71. data/lib/cassandra.rb +41 -0
  72. data/sessionm-cassandra.gemspec +47 -0
  73. data/test/cassandra_client_test.rb +20 -0
  74. data/test/cassandra_mock_test.rb +128 -0
  75. data/test/cassandra_test.rb +1353 -0
  76. data/test/comparable_types_test.rb +45 -0
  77. data/test/composite_type_test.rb +64 -0
  78. data/test/eventmachine_test.rb +42 -0
  79. data/test/ordered_hash_test.rb +386 -0
  80. data/test/test_helper.rb +19 -0
  81. data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
  82. data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
  83. data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
  84. data/vendor/0.7/gen-rb/cassandra.rb +1936 -0
  85. data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
  86. data/vendor/0.7/gen-rb/cassandra_types.rb +681 -0
  87. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  88. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  89. data/vendor/0.8/gen-rb/cassandra_types.rb +824 -0
  90. data/vendor/1.0/gen-rb/cassandra.rb +2215 -0
  91. data/vendor/1.0/gen-rb/cassandra_constants.rb +12 -0
  92. data/vendor/1.0/gen-rb/cassandra_types.rb +857 -0
  93. data/vendor/1.1/gen-rb/cassandra.rb +2571 -0
  94. data/vendor/1.1/gen-rb/cassandra_constants.rb +12 -0
  95. data/vendor/1.1/gen-rb/cassandra_types.rb +928 -0
  96. metadata +287 -0
@@ -0,0 +1,128 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/cassandra_test')
3
+ require 'cassandra/mock'
4
+ require 'json'
5
+
6
+ class CassandraMockTest < CassandraTest
7
+ include Cassandra::Constants
8
+
9
+ def setup
10
+ @test_schema = JSON.parse(File.read(File.join(File.expand_path(File.dirname(__FILE__)), '..','conf', CASSANDRA_VERSION, 'schema.json')))
11
+ @twitter = Cassandra::Mock.new('Twitter', @test_schema)
12
+ @twitter.clear_keyspace!
13
+
14
+ @blogs = Cassandra::Mock.new('Multiblog', @test_schema)
15
+ @blogs.clear_keyspace!
16
+
17
+ @blogs_long = Cassandra::Mock.new('MultiblogLong', @test_schema)
18
+ @blogs_long.clear_keyspace!
19
+
20
+ @type_conversions = Cassandra::Mock.new('TypeConversions', @test_schema)
21
+ @type_conversions.clear_keyspace!
22
+
23
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
24
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
25
+ @composites = [
26
+ Cassandra::Composite.new([5].pack('N'), "zebra"),
27
+ Cassandra::Composite.new([5].pack('N'), "aardvark"),
28
+ Cassandra::Composite.new([1].pack('N'), "elephant"),
29
+ Cassandra::Composite.new([10].pack('N'), "kangaroo"),
30
+ ]
31
+ @dynamic_composites = [
32
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
33
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
34
+ Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
35
+ Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
36
+ ]
37
+ end
38
+
39
+ def test_setup
40
+ assert @twitter
41
+ assert @blogs
42
+ assert @blogs_long
43
+ end
44
+
45
+ def test_schema_for_keyspace
46
+ data = @test_schema['Twitter']
47
+ stuff = @twitter.send(:schema_for_keyspace, 'Twitter')
48
+ data.keys.each do |k|
49
+ assert_equal data[k], stuff[k], k
50
+ end
51
+ end
52
+
53
+ def test_sorting_row_keys
54
+ @twitter.insert(:Statuses, 'b', {:text => 'foo'})
55
+ @twitter.insert(:Statuses, 'a', {:text => 'foo'})
56
+ assert_equal ['a'], @twitter.get_range(:Statuses, :key_count => 1).keys
57
+ end
58
+
59
+ def test_get_range_reversed
60
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
61
+ hash = Cassandra::OrderedHash[data]
62
+ reversed_hash = Cassandra::OrderedHash[data.reverse]
63
+
64
+ @twitter.insert(:Statuses, "all-keys", hash)
65
+
66
+ columns = @twitter.get_range(:Statuses, :reversed => true)["all-keys"]
67
+ columns.each do |column|
68
+ assert_equal reversed_hash.shift, column
69
+ end
70
+ end
71
+
72
+ def test_get_range_reversed_slice
73
+ data = 4.times.map { |i| ["body-#{i.to_s}", "v"] }
74
+ hash = Cassandra::OrderedHash[data]
75
+ sliced_hash = Cassandra::OrderedHash[data.reverse[1..-1]]
76
+
77
+ @twitter.insert(:Statuses, "all-keys", hash)
78
+
79
+ columns = @twitter.get_range(
80
+ :Statuses,
81
+ :start => sliced_hash.keys.first,
82
+ :reversed => true
83
+ )["all-keys"]
84
+
85
+ columns.each do |column|
86
+ assert_equal sliced_hash.shift, column
87
+ end
88
+ end
89
+
90
+ def test_get_range_count
91
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
92
+ hash = Cassandra::OrderedHash[data]
93
+
94
+ @twitter.insert(:Statuses, "all-keys", hash)
95
+
96
+ columns = @twitter.get_range(:Statuses, :count => 2)["all-keys"]
97
+ assert_equal 2, columns.count
98
+ end
99
+
100
+ def test_inserting_array_for_indices
101
+ @twitter.insert(:TimelinishThings, 'a', ['1','2'])
102
+ row = @twitter.get(:TimelinishThings, 'a')
103
+ assert_equal({'1' => nil, '2' => nil}, row)
104
+
105
+ assert_raises(ArgumentError) {
106
+ @twitter.insert(:UserRelationships, 'a', ['u1','u2'])
107
+ }
108
+ end
109
+
110
+ def test_column_timestamps
111
+ base_time = Time.now
112
+ @twitter.insert(:Statuses, "time-key", { "body" => "value" })
113
+
114
+ results = @twitter.get(:Statuses, "time-key")
115
+ assert(results.timestamps["body"] / 1000000 >= base_time.to_i)
116
+ end
117
+
118
+ def test_supercolumn_timestamps
119
+ base_time = Time.now
120
+ @twitter.insert(:StatusRelationships, "time-key", { "super" => { @uuids[1] => "value" }})
121
+
122
+ results = @twitter.get(:StatusRelationships, "time-key")
123
+ assert_nil(results.timestamps["super"])
124
+
125
+ columns = results["super"]
126
+ assert(columns.timestamps[@uuids[1]] / 1000000 >= base_time.to_i)
127
+ end
128
+ end
@@ -0,0 +1,1353 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class CassandraTest < Test::Unit::TestCase
4
+ include Cassandra::Constants
5
+
6
+ def assert_has_keys(keys, hash)
7
+ assert_equal Set.new(keys), Set.new(hash.keys)
8
+ end
9
+
10
+ def setup
11
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :timeout => 5, :exception_classes => [])
12
+ @twitter.clear_keyspace!
13
+
14
+ @blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :timeout => 5, :exception_classes => [])
15
+ @blogs.clear_keyspace!
16
+
17
+ @blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :timeout => 5, :exception_classes => [])
18
+ @blogs_long.clear_keyspace!
19
+
20
+ @type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :timeout => 5, :exception_classes => [])
21
+ @type_conversions.clear_keyspace!
22
+
23
+ Cassandra::WRITE_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
24
+ Cassandra::READ_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
25
+
26
+ @uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
27
+ @longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
28
+ @composites = [
29
+ Cassandra::Composite.new([5].pack('N'), "zebra"),
30
+ Cassandra::Composite.new([5].pack('N'), "aardvark"),
31
+ Cassandra::Composite.new([1].pack('N'), "elephant"),
32
+ Cassandra::Composite.new([10].pack('N'), "kangaroo"),
33
+ ]
34
+ @dynamic_composites = [
35
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
36
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
37
+ Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
38
+ Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
39
+ ]
40
+ end
41
+
42
+ def test_inspect
43
+ assert_nothing_raised do
44
+ @blogs.inspect
45
+ @twitter.inspect
46
+ end
47
+ end
48
+
49
+ def test_setting_default_consistency
50
+ assert_nothing_raised do
51
+ @twitter.default_read_consistency = Cassandra::Consistency::ALL
52
+ end
53
+ assert_equal(Cassandra::READ_DEFAULTS[:consistency], Cassandra::Consistency::ALL)
54
+
55
+ assert_nothing_raised do
56
+ @twitter.default_write_consistency = Cassandra::Consistency::ALL
57
+ end
58
+ assert_equal(Cassandra::WRITE_DEFAULTS[:consistency], Cassandra::Consistency::ALL)
59
+ end
60
+
61
+ def test_get_key
62
+
63
+ @twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
64
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
65
+ assert_equal(['body', 'user'].sort, @twitter.get(:Users, key).timestamps.keys.sort)
66
+ assert_equal({}, @twitter.get(:Users, 'bogus'))
67
+ end
68
+
69
+ def test_get_single_column_returns_single_value
70
+ @twitter.insert(:Users, key, {'body' => 'body_text', 'user' => 'user_name'})
71
+ assert_equal('body_text', @twitter.get(:Users, key, 'body'))
72
+ assert_equal('user_name', @twitter.get(:Users, key, 'user'))
73
+
74
+ @blogs.insert(:Blogs, key,
75
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
76
+
77
+ assert_equal('I like this cat', @blogs.get(:Blogs, key, @uuids[0]))
78
+ end
79
+
80
+ def test_get_key_preserving_order
81
+ # In-order hash is preserved
82
+ hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
83
+ @twitter.insert(:Users, key, hash)
84
+ assert_equal(hash.keys, @twitter.get(:Users, key).keys)
85
+
86
+ @twitter.remove(:Users, key)
87
+
88
+ # Out-of-order hash is returned sorted
89
+ hash = OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
90
+ @twitter.insert(:Users, key, hash)
91
+ assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
92
+ assert_equal(hash.timestamps.keys.sort, @twitter.get(:Users, key).timestamps.keys)
93
+ assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
94
+ end
95
+
96
+ def test_get_first_time_uuid_column
97
+ @blogs.insert(:Blogs, key,
98
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
99
+
100
+ assert_equal({@uuids[0] => 'I like this cat'}, @blogs.get(:Blogs, key, :count => 1))
101
+ assert_equal({@uuids[2] => 'I disagree'}, @blogs.get(:Blogs, key, :count => 1, :reversed => true))
102
+ assert_equal({}, @blogs.get(:Blogs, 'bogus'))
103
+ end
104
+
105
+ def test_get_multiple_time_uuid_columns
106
+ @blogs.insert(:Blogs, key,
107
+ {@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
108
+
109
+ assert_equal(['I like this cat', 'Buttons is cuter'], @blogs.get_columns(:Blogs, key, @uuids[0..1]))
110
+ end
111
+
112
+ def test_get_first_long_column
113
+ @blogs_long.insert(:Blogs, key,
114
+ {@longs[0] => 'I like this cat', @longs[1] => 'Buttons is cuter', @longs[2] => 'I disagree'})
115
+
116
+ assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
117
+ assert_equal({@longs[2] => 'I disagree'}, @blogs_long.get(:Blogs, key, :count => 1, :reversed => true))
118
+ assert_equal({}, @blogs_long.get(:Blogs, 'bogus'))
119
+
120
+ assert_equal([@longs[0]], @blogs_long.get(:Blogs, key, :count => 1).timestamps.keys)
121
+ assert_equal([@longs[2]], @blogs_long.get(:Blogs, key, :count => 1, :reversed => true).timestamps.keys)
122
+ end
123
+
124
+ def test_long_remove_bug
125
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
126
+ @blogs_long.remove(:Blogs, key)
127
+ assert_equal({}, @blogs_long.get(:Blogs, key, :count => 1))
128
+
129
+ @blogs_long.insert(:Blogs, key, {@longs[0] => 'I really like this cat'})
130
+ assert_equal({@longs[0] => 'I really like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
131
+ assert_equal([@longs[0]], @blogs_long.get(:Blogs, key, :count => 1).timestamps.keys)
132
+ end
133
+
134
+ def test_get_with_count
135
+ @twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
136
+ assert_equal 1, @twitter.get(:Statuses, key, :count => 1).size
137
+ assert_equal 2, @twitter.get(:Statuses, key, :count => 2).size
138
+ assert_equal 1, @twitter.get(:Statuses, key, :count => 1).timestamps.size
139
+ assert_equal 2, @twitter.get(:Statuses, key, :count => 2).timestamps.size
140
+ end
141
+
142
+ def test_get_value
143
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
144
+ assert_equal 'v', @twitter.get(:Statuses, key, 'body')
145
+ assert_nil @twitter.get(:Statuses, 'bogus', 'body')
146
+
147
+ assert @twitter.exists?(:Statuses, key, 'body')
148
+ assert !@twitter.exists?(:Statuses, 'bogus', 'body')
149
+ end
150
+
151
+ def test_get_value_with_range
152
+ k = key
153
+
154
+ 10.times do |i|
155
+ @twitter.insert(:Statuses, k, {"body-#{i}" => 'v'})
156
+ end
157
+
158
+ assert_equal 5, @twitter.get(:Statuses, k, :count => 5).length
159
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-5").length
160
+ assert_equal 5, @twitter.get(:Statuses, k, :finish => "body-4").length
161
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-1", :count => 5).length
162
+ assert_equal 5, @twitter.get(:Statuses, k, :start => "body-0", :finish => "body-4", :count => 7).length
163
+ end
164
+
165
+ def test_exists
166
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
167
+ assert_equal true, @twitter.exists?(:Statuses, key)
168
+ assert_equal false, @twitter.exists?(:Statuses, 'bogus')
169
+
170
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
171
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
172
+
173
+ # verify return value when searching by key
174
+ assert_equal true, @twitter.exists?(:StatusRelationships, key)
175
+ assert_equal false, @twitter.exists?(:StatusRelationships, 'bogus')
176
+
177
+ # verify return value when searching by key and column
178
+ assert_equal true, @twitter.exists?(:StatusRelationships, key, 'user_timelines')
179
+ assert_equal false, @twitter.exists?(:StatusRelationships, key, 'bogus')
180
+
181
+ # verify return value when searching by key and column and subcolumn
182
+ assert_equal true, @twitter.exists?(:StatusRelationships, key, 'user_timelines', @uuids[1])
183
+ assert_equal false, @twitter.exists?(:StatusRelationships, key, 'user_timelines', @uuids[3])
184
+ end
185
+
186
+ def test_get_super_key
187
+ columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
188
+ @twitter.insert(:StatusRelationships, key, columns)
189
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
190
+ assert_equal(columns.keys, @twitter.get(:StatusRelationships, key).timestamps.keys)
191
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
192
+ end
193
+
194
+ def test_get_several_super_keys
195
+ columns = OrderedHash[
196
+ 'mentions_timelines', {@uuids[2] => 'v2'},
197
+ 'user_timelines', {@uuids[1] => 'v1'}
198
+ ]
199
+
200
+ @twitter.insert(:StatusRelationships, key, columns)
201
+
202
+ assert_equal(columns, @twitter.get(:StatusRelationships, key))
203
+ assert_equal(columns.keys, @twitter.get(:StatusRelationships, key).timestamps.keys)
204
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
205
+ end
206
+
207
+ def test_get_super_sub_keys_with_count
208
+ @twitter.insert(:StatusRelationships, key,
209
+ {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2', @uuids[3] => 'v3'}})
210
+ assert_equal({@uuids[1] => 'v1'},
211
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1))
212
+ assert_equal({@uuids[3] => 'v3'},
213
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true))
214
+ assert_equal([@uuids[1]],
215
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1).timestamps.keys)
216
+ assert_equal([@uuids[3]],
217
+ @twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true).timestamps.keys)
218
+ end
219
+
220
+ def test_get_super_sub_keys_with_ranges
221
+ @twitter.insert(:StatusRelationships, key,
222
+ {'user_timelines' => {
223
+ @uuids[1] => 'v1',
224
+ @uuids[2] => 'v2',
225
+ @uuids[3] => 'v3',
226
+ @uuids[4] => 'v4',
227
+ @uuids[5] => 'v5'}})
228
+
229
+ keys = @twitter.get(:StatusRelationships, key, "user_timelines").keys
230
+ assert_equal keys.sort, keys
231
+ assert_equal({@uuids[1] => 'v1'}, @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1))
232
+ assert_equal({@uuids[2] => 'v2'}, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1))
233
+ assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).size
234
+ assert_equal([@uuids[1]], @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1).timestamps.keys)
235
+ assert_equal([@uuids[2]], @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1).timestamps.keys)
236
+ assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).timestamps.size
237
+ end
238
+
239
+ def test_get_super_sub_key
240
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
241
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
242
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
243
+ assert_equal(columns.keys.sort, @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps.keys.sort)
244
+ assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
245
+ # FIXME Not sure if this is valid
246
+ assert_equal false, @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
247
+ end
248
+
249
+ def test_get_super_value
250
+ columns = {@uuids[1] => 'v1'}
251
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
252
+ assert_equal('v1', @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first))
253
+ assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
254
+ end
255
+
256
+ # def test_get_range_with_key_range
257
+ # skip('This test requires the use of OrderPreservingPartitioner on the cluster to work properly.')
258
+ # k = key
259
+ # @twitter.insert(:Statuses, k + '2', {'body' => '1'})
260
+ # @twitter.insert(:Statuses, k + '3', {'body' => '1'})
261
+ # @twitter.insert(:Statuses, k + '4', {'body' => '1'})
262
+ # @twitter.insert(:Statuses, k + '5', {'body' => '1'})
263
+ # @twitter.insert(:Statuses, k + '6', {'body' => '1'})
264
+ # assert_equal([k + '3', k + '4', k + '5'], @twitter.get_range(:Statuses, :start_key => k + '3', :finish_key => k + '5').keys)
265
+ # end
266
+
267
+ def test_get_range
268
+ # make sure that deleted rows are not included in the iteration
269
+ 10.times do |i|
270
+ @twitter.insert(:Statuses, i.to_s, {'body' => '1'})
271
+ @twitter.insert(:Statuses, i.to_s + '_delete_me', {'test' => 'value'})
272
+ @twitter.remove(:Statuses, i.to_s + '_delete_me')
273
+ end
274
+
275
+ assert_equal(4, @twitter.get_range_keys(:Statuses, :key_count => 4).size)
276
+ end
277
+
278
+ def test_get_range_with_count
279
+ @twitter.insert(:Statuses, key + '1', {'test_column1' => '1', 'test_column2' => '2', 'test_column3' => '2', 'deleted_column' => '1'})
280
+ @twitter.insert(:Statuses, key + '2', {'test_column4' => '3', 'test_column5' => '4', 'test_column6' => '2', 'deleted_column' => '2'})
281
+
282
+ @twitter.get_range(:Statuses, :count => 3) do |key, columns|
283
+ assert_equal columns.count, 3
284
+ end
285
+
286
+ assert_equal 2, @twitter.get_range(:Statuses, :start_key => key + '1', :finish_key => key + '1', :count => 2)[key + '1'].count
287
+
288
+ @twitter.remove(:Statuses, key + '1', 'deleted_column')
289
+ @twitter.remove(:Statuses, key + '2', 'deleted_column')
290
+
291
+ @twitter.get_range(:Statuses, :count => 2) do |key, columns|
292
+ assert_equal columns.count, 2
293
+ end
294
+
295
+ end
296
+
297
+ def test_get_range_block
298
+ k = key
299
+
300
+ values = {}
301
+ 5.times do |i|
302
+ values[k+i.to_s] = {"body-#{i.to_s}" => 'v'}
303
+ end
304
+
305
+ values.each {|key, columns| @twitter.insert(:Statuses, key, columns) }
306
+
307
+ returned_value = @twitter.get_range(:Statuses, :start_key => k.to_s, :key_count => 5) do |key,columns|
308
+ expected = values.delete(key)
309
+ assert_equal expected, columns
310
+ end
311
+
312
+ assert values.length < 5
313
+ assert_nil returned_value
314
+ end
315
+
316
+ def test_get_range_reversed
317
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
318
+ hash = Cassandra::OrderedHash[data]
319
+ reversed_hash = Cassandra::OrderedHash[data.reverse]
320
+
321
+ @twitter.insert(:Statuses, "all-keys", hash)
322
+
323
+ columns = @twitter.get_range(:Statuses, :reversed => true)["all-keys"]
324
+ columns.each do |column|
325
+ assert_equal reversed_hash.shift, column
326
+ end
327
+ end
328
+
329
+ def test_get_range_with_start_key_and_key_count
330
+ hash = {"name" => "value"}
331
+ @twitter.insert(:Statuses, "a-key", hash)
332
+ @twitter.insert(:Statuses, "b-key", hash)
333
+ @twitter.insert(:Statuses, "c-key", hash)
334
+
335
+ results = @twitter.get_range(:Statuses, :start_key => "b-key", :key_count => 1)
336
+ assert_equal ["b-key"], results.keys
337
+ end
338
+
339
+ def test_each_key
340
+ k = key
341
+ keys_yielded = []
342
+
343
+ 10.times do |i|
344
+ @twitter.insert(:Statuses, k + i.to_s, {"body-#{i.to_s}" => 'v'})
345
+ end
346
+
347
+ # make sure that deleted rows are not included in the iteration
348
+ @twitter.insert(:Statuses, k + '_delete_me', {'test' => 'value'})
349
+ @twitter.remove(:Statuses, k + '_delete_me')
350
+
351
+ @twitter.each_key(:Statuses) do |key|
352
+ keys_yielded << key
353
+ end
354
+
355
+ assert_equal 10, keys_yielded.length
356
+ end
357
+
358
+ def test_each
359
+ k = key
360
+ key_columns = {}
361
+
362
+ 10.times do |i|
363
+ key_columns[k + i.to_s] = {"body-#{i.to_s}" => 'v', 'single_column_lookup' => "value = #{i.to_s}"}
364
+ @twitter.insert(:Statuses, k + i.to_s, key_columns[k + i.to_s])
365
+ end
366
+
367
+ keys_yielded = []
368
+ @twitter.each(:Statuses, :batch_size => 5) do |key, columns|
369
+ assert_equal key_columns[key], columns
370
+ keys_yielded << key
371
+ end
372
+
373
+ assert_equal 10, keys_yielded.length
374
+
375
+ keys_yielded = []
376
+ @twitter.each(:Statuses, :key_count => 7, :batch_size => 5) do |key, columns|
377
+ assert_equal key_columns[key], columns
378
+ keys_yielded << key
379
+ end
380
+
381
+ assert_equal 7, keys_yielded.length, 'each limits to specified count'
382
+
383
+ keys_yielded = []
384
+ @twitter.each(:Statuses, :columns => ['single_column_lookup'], :batch_size => 5) do |key, columns|
385
+ assert_equal key_columns[key].reject {|k2,v| k2 != 'single_column_lookup'}, columns
386
+ keys_yielded << key
387
+ end
388
+
389
+ assert_equal 10, keys_yielded.length
390
+ end
391
+
392
+ def test_multi_get
393
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
394
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
395
+
396
+ expected = OrderedHash[key + '1', {'body' => 'v1', 'user' => 'v1'}, key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}]
397
+ result = @twitter.multi_get(:Users, [key + '1', key + '2', 'bogus'])
398
+ assert_equal expected, result
399
+ assert_equal expected.keys, result.keys
400
+ assert_equal expected.keys.sort, @twitter.multi_get(:Users, [key + '1', key + '2', 'bogus']).timestamps.keys.sort
401
+
402
+ expected = OrderedHash[key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}, key + '1', {'body' => 'v1', 'user' => 'v1'}]
403
+ result = @twitter.multi_get(:Users, [key + '2', 'bogus', key + '1'])
404
+ assert_equal expected, result
405
+ assert_equal expected.keys, result.keys
406
+ assert_equal expected.keys.sort, @twitter.multi_get(:Users, [key + '2', 'bogus', key + '1']).timestamps.keys.sort
407
+ end
408
+
409
+ def test_remove_key
410
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
411
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
412
+
413
+ @twitter.remove(:Statuses, key)
414
+ assert_equal({}, @twitter.get(:Statuses, key))
415
+ end
416
+
417
+ def test_remove_super_sub_key_errors_for_normal_column_family
418
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
419
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
420
+
421
+ assert_raise( ArgumentError) { @twitter.remove(:Statuses, key, 'body' , 'subcolumn') }
422
+ end
423
+
424
+ def test_remove_value
425
+ @twitter.insert(:Statuses, key, {'body' => 'v'})
426
+ @twitter.remove(:Statuses, key, 'body')
427
+ assert_nil @twitter.get(:Statuses, key, 'body')
428
+ assert_nil @twitter.get(:Statuses, key).timestamps['body']
429
+ end
430
+
431
+ def test_remove_super_key
432
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
433
+ @twitter.remove(:StatusRelationships, key)
434
+ assert_equal({}, @twitter.get(:StatusRelationships, key))
435
+ end
436
+
437
+ def test_remove_super_sub_key
438
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
439
+ @twitter.remove(:StatusRelationships, key, 'user_timelines')
440
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
441
+ end
442
+
443
+ def test_remove_super_value
444
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
445
+ column_name_to_remove = @uuids[2]
446
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
447
+ @twitter.remove(:StatusRelationships, key, 'user_timelines', column_name_to_remove)
448
+ assert_equal( columns.reject{|k,v| k == column_name_to_remove}, @twitter.get(:StatusRelationships, key, 'user_timelines') )
449
+ assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps[column_name_to_remove]
450
+ end
451
+
452
+ def test_clear_column_family
453
+ @twitter.insert(:Statuses, key + "1", {'body' => '1'})
454
+ @twitter.insert(:Statuses, key + "2", {'body' => '2'})
455
+ @twitter.insert(:Statuses, key + "3", {'body' => '3'})
456
+ @twitter.clear_column_family!(:Statuses)
457
+ assert_equal 0, @twitter.count_range(:Statuses)
458
+ end
459
+
460
+ def test_insert_key
461
+ @twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
462
+ assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
463
+ assert_equal(['body', 'user'], @twitter.get(:Statuses, key).timestamps.keys)
464
+ end
465
+
466
+ def test_insert_super_key
467
+ columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
468
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
469
+ assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
470
+ assert_equal(columns.keys.sort, @twitter.get(:StatusRelationships, key, 'user_timelines').timestamps.keys.sort)
471
+ end
472
+
473
+ def test_get_columns
474
+ @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
475
+ assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key, ['body', 'user']))
476
+ end
477
+
478
+ def test_get_column_values_super
479
+ user_columns, mentions_columns = {@uuids[1] => 'v1'}, {@uuids[2] => 'v2'}
480
+ @twitter.insert(:StatusRelationships, key,
481
+ {'user_timelines' => user_columns, 'mentions_timelines' => mentions_columns})
482
+ assert_equal [user_columns, mentions_columns],
483
+ @twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
484
+ end
485
+
486
+ def test_get_sub_column_values_super
487
+ user_columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
488
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => user_columns})
489
+ assert_equal ['v1', 'v2'],
490
+ @twitter.get_columns(:StatusRelationships, key, 'user_timelines', @uuids[1..2])
491
+ end
492
+
493
+ def test_multi_get_columns
494
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
495
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
496
+ assert_equal(
497
+ OrderedHash[key + '1', ['v1', 'v1'], key + '2', ['v2', 'v2'], 'bogus', [nil, nil]],
498
+ @twitter.multi_get_columns(:Users, [key + '1', key + '2', 'bogus'], ['body', 'user']))
499
+ assert_equal(
500
+ OrderedHash[key + '2', ['v2', 'v2'], 'bogus', [nil, nil], key + '1', ['v1', 'v1']],
501
+ @twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']))
502
+ assert_equal(
503
+ OrderedHash[key + '1', ['v1', 'v1'], key + '2', ['v2', 'v2'], 'bogus', [nil, nil]].keys.sort,
504
+ @twitter.multi_get_columns(:Users, [key + '1', key + '2', 'bogus'], ['body', 'user']).timestamps.keys.sort)
505
+ assert_equal(
506
+ OrderedHash[key + '2', ['v2', 'v2'], 'bogus', [nil, nil], key + '1', ['v1', 'v1']].keys.sort,
507
+ @twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']).timestamps.keys.sort)
508
+ end
509
+
510
+ def test_count_keys
511
+ k = key
512
+ @twitter.insert(:Statuses, k + "1", {'body' => '1'})
513
+ @twitter.insert(:Statuses, k + "2", {'body' => '2'})
514
+ @twitter.insert(:Statuses, k + "3", {'body' => '3'})
515
+ assert_equal 3, @twitter.count_range(:Statuses)
516
+ end
517
+
518
+ def test_count_columns
519
+ columns = (1..200).inject(Hash.new){|h,v| h['column' + v.to_s] = v.to_s; h;}
520
+
521
+ @twitter.insert(:Statuses, key, columns)
522
+ assert_equal 200, @twitter.count_columns(:Statuses, key, :count => 200)
523
+ assert_equal 100, @twitter.count_columns(:Statuses, key) if CASSANDRA_VERSION.to_f >= 0.8
524
+ assert_equal 55, @twitter.count_columns(:Statuses, key, :count => 55) if CASSANDRA_VERSION.to_f >= 0.8
525
+ end
526
+
527
+ def test_count_super_columns
528
+ @twitter.insert(:StatusRelationships, key, {
529
+ 'user_timelines' => {@uuids[1] => 'v1'},
530
+ 'mentions_timelines' => {@uuids[2] => 'v2'}})
531
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
532
+ end
533
+
534
+ def test_count_super_sub_columns
535
+ @twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'}})
536
+ assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
537
+ end
538
+
539
+ def test_multi_count_columns
540
+ @twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
541
+ @twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
542
+ assert_equal(
543
+ OrderedHash[key + '1', 2, key + '2', 2, 'bogus', 0],
544
+ @twitter.multi_count_columns(:Users, [key + '1', key + '2', 'bogus']))
545
+ assert_equal(
546
+ OrderedHash[key + '2', 2, 'bogus', 0, key + '1', 2],
547
+ @twitter.multi_count_columns(:Users, [key + '2', 'bogus', key + '1']))
548
+ end
549
+
550
+ def test_batch_mutate
551
+ k = key
552
+
553
+ @twitter.insert(:Users, k + '0', {'delete_me' => 'v0', 'keep_me' => 'v0'})
554
+ @twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
555
+ initial_subcolumns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
556
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => initial_subcolumns, 'dummy_supercolumn' => {@uuids[5] => 'value'}})
557
+ assert_equal(initial_subcolumns, @twitter.get(:StatusRelationships, k, 'user_timelines'))
558
+ assert_equal({@uuids[5] => 'value'}, @twitter.get(:StatusRelationships, k, 'dummy_supercolumn'))
559
+ new_subcolumns = {@uuids[3] => 'v3', @uuids[4] => 'v4'}
560
+ subcolumn_to_delete = initial_subcolumns.keys.first # the first column of the initial set
561
+
562
+ @twitter.batch do
563
+ # Normal Columns
564
+ @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
565
+ @twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
566
+ @twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
567
+ @twitter.insert(:Statuses, k + '3', {'body' => 'v'})
568
+ @twitter.add(:UserCounters, 'bob', 5, 'tweet_count') if CASSANDRA_VERSION.to_f >= 0.8
569
+
570
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Written
571
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
572
+ assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
573
+ assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
574
+ assert_equal({}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 # Written
575
+
576
+ @twitter.remove(:Users, k + '1') # Full row
577
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
578
+
579
+ @twitter.remove(:Users, k + '0', 'delete_me') # A single column of the row
580
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Not yet removed
581
+
582
+ @twitter.remove(:Users, k + '4')
583
+ @twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
584
+ assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
585
+
586
+ # SuperColumns
587
+ # Add and delete new sub columns to the user timeline supercolumn
588
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => new_subcolumns })
589
+ @twitter.remove(:StatusRelationships, k, 'user_timelines' , subcolumn_to_delete ) # Delete the first of the initial_subcolumns from the user_timeline supercolumn
590
+ assert_equal(initial_subcolumns, @twitter.get(:StatusRelationships, k, 'user_timelines')) # No additions or deletes reflected yet
591
+ # Delete a complete supercolumn
592
+ @twitter.remove(:StatusRelationships, k, 'dummy_supercolumn' ) # Delete the full dummy supercolumn
593
+ assert_equal({@uuids[5] => 'value'}, @twitter.get(:StatusRelationships, k, 'dummy_supercolumn')) # dummy supercolumn not yet deleted
594
+ end
595
+
596
+ assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
597
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}, @twitter.get(:Users, k + '3')) # Written and compacted
598
+ assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
599
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
600
+ assert_equal({'tweet_count' => 5}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 # Written
601
+ assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
602
+
603
+ assert_equal({ 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # 'delete_me' column removed
604
+
605
+
606
+ assert_equal({'body' => 'v2', 'user' => 'v2'}.keys.sort, @twitter.get(:Users, k + '2').timestamps.keys.sort) # Written
607
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}.keys.sort, @twitter.get(:Users, k + '3').timestamps.keys.sort) # Written and compacted
608
+ assert_equal({'body' => 'v4', 'user' => 'v4'}.keys.sort, @twitter.get(:Users, k + '4').timestamps.keys.sort) # Written
609
+ assert_equal({'body' => 'v'}.keys.sort, @twitter.get(:Statuses, k + '3').timestamps.keys.sort) # Written
610
+
611
+ # Final result: initial_subcolumns - initial_subcolumns.first + new_subcolumns
612
+ resulting_subcolumns = initial_subcolumns.merge(new_subcolumns).reject{|k2,v| k2 == subcolumn_to_delete }
613
+ assert_equal(resulting_subcolumns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
614
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'dummy_supercolumn')) # dummy supercolumn deleted
615
+
616
+ end
617
+
618
+ def test_batch_queue_size
619
+ k = key
620
+
621
+ @twitter.insert(:Users, k + '0', {'delete_me' => 'v0', 'keep_me' => 'v0'})
622
+ @twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
623
+ initial_subcolumns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
624
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => initial_subcolumns, 'dummy_supercolumn' => {@uuids[5] => 'value'}})
625
+ assert_equal(initial_subcolumns, @twitter.get(:StatusRelationships, k, 'user_timelines'))
626
+ assert_equal({@uuids[5] => 'value'}, @twitter.get(:StatusRelationships, k, 'dummy_supercolumn'))
627
+ new_subcolumns = {@uuids[3] => 'v3', @uuids[4] => 'v4'}
628
+ subcolumn_to_delete = initial_subcolumns.keys.first # the first column of the initial set
629
+
630
+ @twitter.batch(:queue_size => 5) do
631
+ # Normal Columns
632
+ @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
633
+ @twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
634
+ @twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
635
+ @twitter.insert(:Statuses, k + '3', {'body' => 'v'})
636
+
637
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Written
638
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
639
+ assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
640
+ assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
641
+ assert_equal({}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 # Written
642
+
643
+ if CASSANDRA_VERSION.to_f >= 0.8
644
+ @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
645
+ else
646
+ @twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
647
+ end
648
+ # Flush!
649
+
650
+ assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
651
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}, @twitter.get(:Users, k + '3')) # Written and compacted
652
+ assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
653
+ assert_equal({'tweet_count' => 5}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 # Written
654
+
655
+ @twitter.remove(:Users, k + '1') # Full row
656
+ @twitter.remove(:Users, k + '0', 'delete_me') # A single column of the row
657
+ @twitter.remove(:Users, k + '4')
658
+ @twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
659
+
660
+ assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
661
+ assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Not yet removed
662
+ assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
663
+
664
+ @twitter.insert(:Users, k + '5', {'body' => 'v5', 'user' => 'v5'})
665
+ # Flush!
666
+
667
+ assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
668
+ assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
669
+ assert_equal({ 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # 'delete_me' column removed
670
+
671
+ assert_equal({'body' => 'v2', 'user' => 'v2'}.keys.sort, @twitter.get(:Users, k + '2').timestamps.keys.sort) # Written
672
+ assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}.keys.sort, @twitter.get(:Users, k + '3').timestamps.keys.sort) # Written and compacted
673
+ assert_equal({'body' => 'v4', 'user' => 'v4'}.keys.sort, @twitter.get(:Users, k + '4').timestamps.keys.sort) # Written
674
+ assert_equal({'body' => 'v'}.keys.sort, @twitter.get(:Statuses, k + '3').timestamps.keys.sort) # Written
675
+
676
+ # SuperColumns
677
+ # Add and delete new sub columns to the user timeline supercolumn
678
+ @twitter.insert(:StatusRelationships, k, {'user_timelines' => new_subcolumns })
679
+ @twitter.remove(:StatusRelationships, k, 'user_timelines' , subcolumn_to_delete ) # Delete the first of the initial_subcolumns from the user_timeline supercolumn
680
+ # Delete a complete supercolumn
681
+ @twitter.remove(:StatusRelationships, k, 'dummy_supercolumn' ) # Delete the full dummy supercolumn
682
+ end
683
+
684
+ # Final result: initial_subcolumns - initial_subcolumns.first + new_subcolumns
685
+ resulting_subcolumns = initial_subcolumns.merge(new_subcolumns).reject{|k2,v| k2 == subcolumn_to_delete }
686
+ assert_equal(resulting_subcolumns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
687
+ assert_equal({}, @twitter.get(:StatusRelationships, key, 'dummy_supercolumn')) # dummy supercolumn deleted
688
+ end
689
+
690
+ def test_complain_about_nil_key
691
+ assert_raises(ArgumentError) do
692
+ @twitter.insert(:Statuses, nil, {'text' => 'crap'})
693
+ end
694
+ end
695
+
696
+ def test_nil_sub_column_value
697
+ @twitter.insert(:Indexes, 'asdf', {"thing" => {'jkl' => ''} })
698
+ end
699
+
700
+ def test_disconnect!
701
+ @twitter.disconnect!
702
+ assert_nil @twitter.instance_variable_get(:@client)
703
+ end
704
+
705
+ def test_disconnect_when_not_connected!
706
+ assert_nothing_raised do
707
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
708
+ @twitter.disconnect!
709
+ end
710
+ end
711
+
712
+ def test_super_allows_for_non_string_values_while_normal_does_not
713
+ columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
714
+
715
+ @twitter.insert(:StatusRelationships, key, columns)
716
+ @twitter.insert(:Statuses, key, { 'body' => '1' })
717
+ end
718
+
719
+ def test_batch_over_deletes
720
+ k = key
721
+
722
+ @twitter.batch do
723
+ @twitter.insert(:Users, k, {'body' => 'body', 'user' => 'user'})
724
+ @twitter.remove(:Users, k, 'body')
725
+ end
726
+
727
+ assert_equal({'user' => 'user'}, @twitter.get(:Users, k))
728
+ end
729
+
730
+ def test_each_key
731
+ num_users = rand(60)
732
+ num_users.times do |twit_counter|
733
+ @twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
734
+ end
735
+ counter = 0
736
+ @twitter.each_key(:Users) do |_, _|
737
+ counter += 1
738
+ end
739
+ assert_equal num_users, counter
740
+ end
741
+
742
+ def test_each_with_column_predicate
743
+ num_users = rand(60)
744
+ num_users.times do |twit_counter|
745
+ @twitter.insert(:Users, "Twitter : #{twit_counter}", {'body' => 'v1', 'user' => 'v1'})
746
+ end
747
+ counter = 0
748
+ @twitter.each(:Users, :batch_size => 10, :start => 'body', :finish => 'body') do |key, columns|
749
+ assert_equal 1, columns.length
750
+ counter += 1
751
+ end
752
+ assert_equal num_users, counter
753
+ end
754
+
755
+ def test_each_with_super_column
756
+ num_users = rand(50)
757
+ block_name = key
758
+ num_users.times do |twit_counter|
759
+ @twitter.insert(:StatusRelationships, block_name + twit_counter.to_s, {
760
+ 'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'},
761
+ 'mentions_timelines' => {@uuids[3] => 'v3'}})
762
+ end
763
+
764
+ counter = 0
765
+ # Restrict to one super column ::
766
+ @twitter.each(:StatusRelationships, :batch_size => 10, :start => 'user_timelines', :finish => 'user_timelines') do |key, columns|
767
+ columns.each do |_, column_value|
768
+ assert_equal 2, column_value.length
769
+ end
770
+ counter += 1
771
+ end
772
+
773
+ #Both super columns
774
+ @twitter.each(:StatusRelationships, :batch_size => 10, :start => 'mentions_timelines', :finish => 'user_timelines') do |key,columns|
775
+ assert_equal 2, columns.length
776
+ counter += 1
777
+ end
778
+
779
+ assert_equal num_users*2, counter
780
+
781
+ end
782
+
783
+ def test_each_column_types
784
+ num_users = rand(60)
785
+ num_users.times do |twit_counter|
786
+ @type_conversions.insert(:UUIDColumnConversion, twit_counter.to_s, {@uuids[1] => 'v1'})
787
+ end
788
+ counter = 0
789
+ @type_conversions.each(:UUIDColumnConversion) do |_, columns|
790
+ counter += 1
791
+ columns.keys.each {|column_name| assert_equal SimpleUUID::UUID, column_name.class}
792
+ end
793
+ assert_equal num_users, counter
794
+ end
795
+
796
+
797
+ if CASSANDRA_VERSION.to_f >= 0.7
798
+ def test_creating_and_dropping_new_index
799
+ @twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
800
+ assert_nil @twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
801
+
802
+ @twitter.drop_index('Twitter', 'Statuses', 'column_name')
803
+ assert_nil @twitter.drop_index('Twitter', 'Statuses', 'column_name')
804
+
805
+ # Recreating and redropping the same index should not error either.
806
+ @twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
807
+ @twitter.drop_index('Twitter', 'Statuses', 'column_name')
808
+ end
809
+
810
+ def test_get_indexed_slices
811
+ @twitter.insert(:Statuses, 'row_a', {
812
+ 'tags' => 'a',
813
+ 'y' => 'foo'
814
+ })
815
+ @twitter.insert(:Statuses, 'row_b', {
816
+ 'tags' => 'b',
817
+ 'y' => 'foo'
818
+ })
819
+ [1,2].each do |i|
820
+ @twitter.insert(:Statuses, "row_c_#{i}", {
821
+ 'tags' => 'c',
822
+ 'y' => 'a'
823
+ })
824
+ end
825
+ [3,4].each do |i|
826
+ @twitter.insert(:Statuses, "row_c_#{i}", {
827
+ 'tags' => 'c',
828
+ 'y' => 'b'
829
+ })
830
+ end
831
+ @twitter.insert(:Statuses, 'row_d', {
832
+ 'tags' => 'd',
833
+ 'y' => 'foo'
834
+ })
835
+ @twitter.insert(:Statuses, 'row_e', {
836
+ 'tags' => 'e',
837
+ 'y' => 'bar'
838
+ })
839
+
840
+ # Test == operator (single clause)
841
+ rows = @twitter.get_indexed_slices(:Statuses, [
842
+ {:column_name => 'tags',
843
+ :value => 'c',
844
+ :comparison => "=="}
845
+ ])
846
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
847
+
848
+ # Test other operators
849
+ # Currently (as of Cassandra 1.1) you can't use anything but == as the
850
+ # primary query -- you must match on == first, and subsequent clauses are
851
+ # then applied as filters -- so that's what we are doing here.
852
+
853
+ # Test > operator
854
+ rows = @twitter.get_indexed_slices(:Statuses, [
855
+ {:column_name => 'tags',
856
+ :value => 'c',
857
+ :comparison => "=="},
858
+ {:column_name => 'y',
859
+ :value => 'a',
860
+ :comparison => ">"}
861
+ ])
862
+ assert_has_keys %w[row_c_3 row_c_4], rows
863
+
864
+ # Test >= operator
865
+ rows = @twitter.get_indexed_slices(:Statuses, [
866
+ {:column_name => 'tags',
867
+ :value => 'c',
868
+ :comparison => "=="},
869
+ {:column_name => 'y',
870
+ :value => 'a',
871
+ :comparison => ">="}
872
+ ])
873
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
874
+
875
+ # Test < operator
876
+ rows = @twitter.get_indexed_slices(:Statuses, [
877
+ {:column_name => 'tags',
878
+ :value => 'c',
879
+ :comparison => "=="},
880
+ {:column_name => 'y',
881
+ :value => 'b',
882
+ :comparison => "<"}
883
+ ])
884
+ assert_has_keys %w[row_c_1 row_c_2], rows
885
+
886
+ # Test <= operator
887
+ rows = @twitter.get_indexed_slices(:Statuses, [
888
+ {:column_name => 'tags',
889
+ :value => 'c',
890
+ :comparison => "=="},
891
+ {:column_name => 'y',
892
+ :value => 'b',
893
+ :comparison => "<="}
894
+ ])
895
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
896
+
897
+ # Test query on a non-indexed column
898
+ unless self.is_a?(CassandraMockTest)
899
+ assert_raises(CassandraThrift::InvalidRequestException) do
900
+ @twitter.get_indexed_slices(:Statuses, [
901
+ {:column_name => 'y',
902
+ :value => 'foo',
903
+ :comparison => "=="}
904
+ ])
905
+ end
906
+ end
907
+
908
+ # Test start key
909
+ rows = @twitter.get_indexed_slices(:Statuses, [
910
+ {:column_name => 'tags',
911
+ :value => 'c',
912
+ :comparison => "=="},
913
+ ], :start_key => 'row_c_2')
914
+ assert_equal 'row_c_2', rows.keys.first
915
+ # <- can't test any keys after that since it's going to be random
916
+
917
+ # Test key_start option
918
+ rows = @twitter.get_indexed_slices(:Statuses, [
919
+ {:column_name => 'tags',
920
+ :value => 'c',
921
+ :comparison => "=="},
922
+ ], :key_start => 'row_c_2')
923
+ assert_equal 'row_c_2', rows.keys.first
924
+ # <- can't test any keys after that since it's going to be random
925
+
926
+ # Test key count
927
+ rows = @twitter.get_indexed_slices(:Statuses, [
928
+ {:column_name => 'tags',
929
+ :value => 'c',
930
+ :comparison => "=="}
931
+ ], :key_count => 2)
932
+ assert_equal 2, rows.length
933
+ # <- can't test which keys are present since it's going to be random
934
+ end
935
+
936
+ def test_get_indexed_slices_with_IndexClause_objects
937
+ @twitter.insert(:Statuses, 'row_a', {
938
+ 'tags' => 'a',
939
+ 'y' => 'foo'
940
+ })
941
+ @twitter.insert(:Statuses, 'row_b', {
942
+ 'tags' => 'b',
943
+ 'y' => 'foo'
944
+ })
945
+ [1,2].each do |i|
946
+ @twitter.insert(:Statuses, "row_c_#{i}", {
947
+ 'tags' => 'c',
948
+ 'y' => 'a'
949
+ })
950
+ end
951
+ [3,4].each do |i|
952
+ @twitter.insert(:Statuses, "row_c_#{i}", {
953
+ 'tags' => 'c',
954
+ 'y' => 'b'
955
+ })
956
+ end
957
+ @twitter.insert(:Statuses, 'row_d', {
958
+ 'tags' => 'd',
959
+ 'y' => 'foo'
960
+ })
961
+ @twitter.insert(:Statuses, 'row_e', {
962
+ 'tags' => 'e',
963
+ 'y' => 'bar'
964
+ })
965
+
966
+ # Test == operator (single clause)
967
+ index_clause = @twitter.create_index_clause([
968
+ @twitter.create_index_expression('tags', 'c', '==')
969
+ ])
970
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
971
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
972
+
973
+ # Test other operators
974
+ # Currently (as of Cassandra 1.1) you can't use anything but == as the
975
+ # primary query -- you must match on == first, and subsequent clauses are
976
+ # then applied as filters -- so that's what we are doing here.
977
+
978
+ # Test > operator
979
+ index_clause = @twitter.create_index_clause([
980
+ @twitter.create_index_expression('tags', 'c', '=='),
981
+ @twitter.create_index_expression('y', 'a', '>'),
982
+ ])
983
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
984
+ assert_has_keys %w[row_c_3 row_c_4], rows
985
+
986
+ # Test >= operator
987
+ index_clause = @twitter.create_index_clause([
988
+ @twitter.create_index_expression('tags', 'c', '=='),
989
+ @twitter.create_index_expression('y', 'a', '>=')
990
+ ])
991
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
992
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
993
+
994
+ # Test < operator
995
+ index_clause = @twitter.create_index_clause([
996
+ @twitter.create_index_expression('tags', 'c', '=='),
997
+ @twitter.create_index_expression('y', 'b', '<')
998
+ ])
999
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
1000
+ assert_has_keys %w[row_c_1 row_c_2], rows
1001
+
1002
+ # Test <= operator
1003
+ index_clause = @twitter.create_index_clause([
1004
+ @twitter.create_index_expression('tags', 'c', '=='),
1005
+ @twitter.create_index_expression('y', 'b', '<=')
1006
+ ])
1007
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
1008
+ assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
1009
+
1010
+ # Test query on a non-indexed column
1011
+ unless self.is_a?(CassandraMockTest)
1012
+ assert_raises(CassandraThrift::InvalidRequestException) do
1013
+ index_clause = @twitter.create_index_clause([
1014
+ @twitter.create_index_expression('y', 'foo', '==')
1015
+ ])
1016
+ @twitter.get_indexed_slices(:Statuses, index_clause)
1017
+ end
1018
+ end
1019
+
1020
+ # Test start key
1021
+ index_clause = @twitter.create_index_clause([
1022
+ @twitter.create_index_expression('tags', 'c', '==')
1023
+ ], 'row_c_2')
1024
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
1025
+ assert_equal 'row_c_2', rows.keys.first
1026
+ # <- can't test any keys after that since it's going to be random
1027
+
1028
+ # Test key count
1029
+ index_clause = @twitter.create_index_clause([
1030
+ @twitter.create_index_expression('tags', 'c', '==')
1031
+ ], "", 2)
1032
+ rows = @twitter.get_indexed_slices(:Statuses, index_clause)
1033
+ assert_equal 2, rows.length
1034
+ # <- can't test which keys are present since it's going to be random
1035
+ end
1036
+
1037
+ def test_create_index_clause
1038
+ return if self.is_a?(CassandraMockTest)
1039
+
1040
+ ie = CassandraThrift::IndexExpression.new(
1041
+ :column_name => 'foo',
1042
+ :value => 'x',
1043
+ :op => '=='
1044
+ )
1045
+
1046
+ ic = @twitter.create_index_clause([ie], 'aaa', 250)
1047
+ assert_instance_of CassandraThrift::IndexClause, ic
1048
+ assert_equal 'aaa', ic.start_key
1049
+ assert_equal ie, ic.expressions[0]
1050
+ assert_equal 250, ic.count
1051
+
1052
+ # test alias
1053
+ ic = @twitter.create_idx_clause([ie], 'aaa', 250)
1054
+ assert_instance_of CassandraThrift::IndexClause, ic
1055
+ assert_equal 'aaa', ic.start_key
1056
+ assert_equal ie, ic.expressions[0]
1057
+ assert_equal 250, ic.count
1058
+ end
1059
+
1060
+ def test_create_index_expression
1061
+ return if self.is_a?(CassandraMockTest)
1062
+
1063
+ # EQ operator
1064
+ [nil, "EQ", "eq", "=="].each do |op|
1065
+ ie = @twitter.create_index_expression('foo', 'x', op)
1066
+ assert_instance_of CassandraThrift::IndexExpression, ie
1067
+ assert_equal 'foo', ie.column_name
1068
+ assert_equal 'x', ie.value
1069
+ assert_equal CassandraThrift::IndexOperator::EQ, ie.op
1070
+ end
1071
+ # alias
1072
+ [nil, "EQ", "eq", "=="].each do |op|
1073
+ ie = @twitter.create_idx_expr('foo', 'x', op)
1074
+ assert_instance_of CassandraThrift::IndexExpression, ie
1075
+ assert_equal 'foo', ie.column_name
1076
+ assert_equal 'x', ie.value
1077
+ assert_equal CassandraThrift::IndexOperator::EQ, ie.op
1078
+ end
1079
+
1080
+ # GTE operator
1081
+ ["GTE", "gte", ">="].each do |op|
1082
+ ie = @twitter.create_index_expression('foo', 'x', op)
1083
+ assert_instance_of CassandraThrift::IndexExpression, ie
1084
+ assert_equal 'foo', ie.column_name
1085
+ assert_equal 'x', ie.value
1086
+ assert_equal CassandraThrift::IndexOperator::GTE, ie.op
1087
+ end
1088
+ # alias
1089
+ ["GTE", "gte", ">="].each do |op|
1090
+ ie = @twitter.create_idx_expr('foo', 'x', op)
1091
+ assert_instance_of CassandraThrift::IndexExpression, ie
1092
+ assert_equal 'foo', ie.column_name
1093
+ assert_equal 'x', ie.value
1094
+ assert_equal CassandraThrift::IndexOperator::GTE, ie.op
1095
+ end
1096
+
1097
+ # GT operator
1098
+ ["GT", "gt", ">"].each do |op|
1099
+ ie = @twitter.create_index_expression('foo', 'x', op)
1100
+ assert_instance_of CassandraThrift::IndexExpression, ie
1101
+ assert_equal 'foo', ie.column_name
1102
+ assert_equal 'x', ie.value
1103
+ assert_equal CassandraThrift::IndexOperator::GT, ie.op
1104
+ end
1105
+ # alias
1106
+ ["GT", "gt", ">"].each do |op|
1107
+ ie = @twitter.create_idx_expr('foo', 'x', op)
1108
+ assert_instance_of CassandraThrift::IndexExpression, ie
1109
+ assert_equal 'foo', ie.column_name
1110
+ assert_equal 'x', ie.value
1111
+ assert_equal CassandraThrift::IndexOperator::GT, ie.op
1112
+ end
1113
+
1114
+ # LTE operator
1115
+ ["LTE", "lte", "<="].each do |op|
1116
+ ie = @twitter.create_index_expression('foo', 'x', op)
1117
+ assert_instance_of CassandraThrift::IndexExpression, ie
1118
+ assert_equal 'foo', ie.column_name
1119
+ assert_equal 'x', ie.value
1120
+ assert_equal CassandraThrift::IndexOperator::LTE, ie.op
1121
+ end
1122
+ # alias
1123
+ ["LTE", "lte", "<="].each do |op|
1124
+ ie = @twitter.create_idx_expr('foo', 'x', op)
1125
+ assert_instance_of CassandraThrift::IndexExpression, ie
1126
+ assert_equal 'foo', ie.column_name
1127
+ assert_equal 'x', ie.value
1128
+ assert_equal CassandraThrift::IndexOperator::LTE, ie.op
1129
+ end
1130
+
1131
+ # LT operator
1132
+ ["LT", "lt", "<"].each do |op|
1133
+ ie = @twitter.create_index_expression('foo', 'x', op)
1134
+ assert_instance_of CassandraThrift::IndexExpression, ie
1135
+ assert_equal 'foo', ie.column_name
1136
+ assert_equal 'x', ie.value
1137
+ assert_equal CassandraThrift::IndexOperator::LT, ie.op
1138
+ end
1139
+ # alias
1140
+ ["LT", "lt", "<"].each do |op|
1141
+ ie = @twitter.create_idx_expr('foo', 'x', op)
1142
+ assert_instance_of CassandraThrift::IndexExpression, ie
1143
+ assert_equal 'foo', ie.column_name
1144
+ assert_equal 'x', ie.value
1145
+ assert_equal CassandraThrift::IndexOperator::LT, ie.op
1146
+ end
1147
+
1148
+ # unknown operator
1149
+ ie = @twitter.create_index_expression('foo', 'x', '~$')
1150
+ assert_equal nil, ie.op
1151
+ # alias
1152
+ ie = @twitter.create_idx_expr('foo', 'x', '~$')
1153
+ assert_equal nil, ie.op
1154
+ end
1155
+
1156
+ def test_column_family_mutation
1157
+ k = key
1158
+
1159
+ if @twitter.column_families.include?(k)
1160
+ @twitter.drop_column_family(k)
1161
+ end
1162
+
1163
+ # Verify add_column_family works as desired.
1164
+ @twitter.add_column_family(
1165
+ Cassandra::ColumnFamily.new(
1166
+ :keyspace => 'Twitter',
1167
+ :name => k
1168
+ )
1169
+ )
1170
+ assert @twitter.column_families.include?(k)
1171
+
1172
+ if CASSANDRA_VERSION.to_f == 0.7
1173
+ # Verify rename_column_family works properly
1174
+ @twitter.rename_column_family(k, k + '_renamed')
1175
+ assert @twitter.column_families.include?(k + '_renamed')
1176
+
1177
+ # Change it back and validate
1178
+ @twitter.rename_column_family(k + '_renamed', k)
1179
+ assert @twitter.column_families.include?(k)
1180
+ end
1181
+
1182
+ temp_cf_def = @twitter.column_families[k]
1183
+ temp_cf_def.comment = k
1184
+ @twitter.update_column_family(temp_cf_def)
1185
+ assert @twitter.column_families.include?(k)
1186
+
1187
+ @twitter.drop_column_family(k)
1188
+ assert !@twitter.column_families.include?(k)
1189
+ end
1190
+ end
1191
+
1192
+ if CASSANDRA_VERSION.to_f >= 0.8
1193
+ def test_adding_getting_value_in_counter
1194
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
1195
+ assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
1196
+ assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
1197
+ end
1198
+
1199
+ def test_get_counter_slice
1200
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
1201
+ assert_equal({'tweet_count' => 5}, @twitter.get(:UserCounters, 'bob', :start => "tweet_count", :finish => "tweet_count"))
1202
+ end
1203
+
1204
+ def test_adding_getting_value_in_multiple_counters
1205
+ assert_nil @twitter.add(:UserCounters, 'bob', 5, 'tweet_count')
1206
+ assert_nil @twitter.add(:UserCounters, 'bob', 7, 'follower_count')
1207
+ assert_equal(5, @twitter.get(:UserCounters, 'bob', 'tweet_count'))
1208
+ assert_nil @twitter.get(:UserCounters, 'bogus', 'tweet_count')
1209
+ assert_equal([5, 7], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count']))
1210
+ assert_equal([5, 7, nil], @twitter.get_columns(:UserCounters, 'bob', ['tweet_count', 'follower_count', 'bogus']))
1211
+ end
1212
+
1213
+ def test_adding_getting_value_in_multiple_counters_with_super_columns
1214
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 1, 'DAU', 'today')
1215
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 2, 'DAU', 'tomorrow')
1216
+ assert_equal(1, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'today'))
1217
+ assert_equal(2, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'tomorrow'))
1218
+ end
1219
+
1220
+ def test_reading_rows_with_super_column_counter
1221
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 1, 'DAU', 'today')
1222
+ assert_nil @twitter.add(:UserCounterAggregates, 'bob', 2, 'DAU', 'tomorrow')
1223
+ result = @twitter.get(:UserCounterAggregates, 'bob')
1224
+ assert_equal(1, result.size)
1225
+ assert_equal(2, result.first.size)
1226
+ assert_equal("DAU", result.first[0])
1227
+ assert_equal(1, result.first[1]["today"])
1228
+ assert_equal(2, result.first[1]["tomorrow"])
1229
+ end
1230
+
1231
+ def test_composite_column_type_conversion
1232
+ columns = {}
1233
+ @composites.each_with_index do |c, index|
1234
+ columns[c] = "value-#{index}"
1235
+ end
1236
+ @type_conversions.insert(:CompositeColumnConversion, key, columns)
1237
+ columns_in_order = [
1238
+ Cassandra::Composite.new([1].pack('N'), "elephant"),
1239
+ Cassandra::Composite.new([5].pack('N'), "aardvark"),
1240
+ Cassandra::Composite.new([5].pack('N'), "zebra"),
1241
+ Cassandra::Composite.new([10].pack('N'), "kangaroo"),
1242
+ ]
1243
+ assert_equal(columns_in_order, @type_conversions.get(:CompositeColumnConversion, key).keys)
1244
+
1245
+ column_slice = @type_conversions.get(:CompositeColumnConversion, key,
1246
+ :start => Cassandra::Composite.new([1].pack('N')),
1247
+ :finish => Cassandra::Composite.new([10].pack('N'))
1248
+ ).keys
1249
+ assert_equal(columns_in_order[0..-2], column_slice)
1250
+
1251
+ column_slice = @type_conversions.get(:CompositeColumnConversion, key,
1252
+ :start => Cassandra::Composite.new([5].pack('N')),
1253
+ :finish => Cassandra::Composite.new([5].pack('N'), :slice => :after)
1254
+ ).keys
1255
+ assert_equal(columns_in_order[1..2], column_slice)
1256
+
1257
+ column_slice = @type_conversions.get(:CompositeColumnConversion, key,
1258
+ :start => Cassandra::Composite.new([5].pack('N'), :slice => :after).to_s
1259
+ ).keys
1260
+ assert_equal([columns_in_order[-1]], column_slice)
1261
+
1262
+ column_slice = @type_conversions.get(:CompositeColumnConversion, key,
1263
+ :finish => Cassandra::Composite.new([10].pack('N'), :slice => :before).to_s
1264
+ ).keys
1265
+ assert_equal(columns_in_order[0..-2], column_slice)
1266
+
1267
+ assert_equal('value-2', @type_conversions.get(:CompositeColumnConversion, key, columns_in_order.first))
1268
+ end
1269
+
1270
+ def test_dynamic_composite_column_type_conversion
1271
+ columns = {}
1272
+ @dynamic_composites.each_with_index do |c, index|
1273
+ columns[c] = "value-#{index}"
1274
+ end
1275
+ @type_conversions.insert(:DynamicComposite, key, columns)
1276
+
1277
+ columns_in_order = [
1278
+ Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
1279
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
1280
+ Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
1281
+ Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
1282
+ ]
1283
+ assert_equal(columns_in_order, @type_conversions.get(:DynamicComposite, key).keys)
1284
+
1285
+ column_slice = @type_conversions.get(:DynamicComposite, key,
1286
+ :start => Cassandra::DynamicComposite.new(['i', [1].pack('N')]),
1287
+ :finish => Cassandra::DynamicComposite.new(['i', [10].pack('N')])
1288
+ ).keys
1289
+ assert_equal(columns_in_order[0..-2], column_slice)
1290
+
1291
+ column_slice = @type_conversions.get(:DynamicComposite, key,
1292
+ :start => Cassandra::DynamicComposite.new(['IntegerType', [5].pack('N')]),
1293
+ :finish => Cassandra::DynamicComposite.new(['IntegerType', [5].pack('N')], :slice => :after)
1294
+ ).keys
1295
+ assert_equal(columns_in_order[1..2], column_slice)
1296
+
1297
+ column_slice = @type_conversions.get(:DynamicComposite, key,
1298
+ :start => Cassandra::DynamicComposite.new(['i', [5].pack('N')], :slice => :after).to_s
1299
+ ).keys
1300
+ assert_equal([columns_in_order[-1]], column_slice)
1301
+
1302
+ column_slice = @type_conversions.get(:DynamicComposite, key,
1303
+ :finish => Cassandra::DynamicComposite.new(['i', [10].pack('N')], :slice => :before).to_s
1304
+ ).keys
1305
+ assert_equal(columns_in_order[0..-2], column_slice)
1306
+
1307
+ assert_equal('value-2', @type_conversions.get(:DynamicComposite, key, columns_in_order.first))
1308
+ end
1309
+ end
1310
+
1311
+ def test_column_timestamps
1312
+ base_time = Time.now
1313
+ @twitter.insert(:Statuses, "time-key", { "body" => "value" })
1314
+
1315
+ results = @twitter.get(:Statuses, "time-key")
1316
+ assert(results.timestamps["body"] / 1000000 >= base_time.to_i)
1317
+ end
1318
+
1319
+ def test_supercolumn_timestamps
1320
+ base_time = Time.now
1321
+ @twitter.insert(:StatusRelationships, "time-key", { "super" => { @uuids[1] => "value" }})
1322
+
1323
+ results = @twitter.get(:StatusRelationships, "time-key")
1324
+ assert_nil(results.timestamps["super"])
1325
+
1326
+ columns = results["super"]
1327
+ assert(columns.timestamps[@uuids[1]] / 1000000 >= base_time.to_i)
1328
+ end
1329
+
1330
+ def test_keyspace_operations
1331
+ system = Cassandra.new 'system'
1332
+ keyspace_name = 'robots'
1333
+ keyspace_definition = Cassandra::Keyspace.new :name => keyspace_name,
1334
+ :strategy_class => 'SimpleStrategy',
1335
+ :strategy_options => { 'replication_factor' => '2' },
1336
+ :cf_defs => []
1337
+ system.add_keyspace keyspace_definition
1338
+ assert system.keyspaces.any? {|it| it == keyspace_name }
1339
+
1340
+ system.drop_keyspace keyspace_name
1341
+ assert system.keyspaces.none? {|it| it == keyspace_name }
1342
+
1343
+ system.add_keyspace keyspace_definition
1344
+ Cassandra.new(keyspace_name).drop_keyspace
1345
+ assert system.keyspaces.none? {|it| it == keyspace_name }
1346
+ end
1347
+
1348
+ private
1349
+
1350
+ def key
1351
+ caller.first[/`(.*?)'/, 1]
1352
+ end
1353
+ end