cassandra-mavericks 0.21.1

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 (109) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG +150 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE +202 -0
  5. data/Manifest +94 -0
  6. data/README.md +373 -0
  7. data/Rakefile +191 -0
  8. data/bin/cassandra_helper +16 -0
  9. data/cassandra.gemspec +29 -0
  10. data/conf/0.6/cassandra.in.sh +47 -0
  11. data/conf/0.6/log4j.properties +38 -0
  12. data/conf/0.6/schema.json +57 -0
  13. data/conf/0.6/storage-conf.xml +352 -0
  14. data/conf/0.7/cassandra.in.sh +46 -0
  15. data/conf/0.7/cassandra.yaml +336 -0
  16. data/conf/0.7/log4j-server.properties +41 -0
  17. data/conf/0.7/schema.json +57 -0
  18. data/conf/0.7/schema.txt +45 -0
  19. data/conf/0.8/cassandra.in.sh +41 -0
  20. data/conf/0.8/cassandra.yaml +61 -0
  21. data/conf/0.8/log4j-server.properties +40 -0
  22. data/conf/0.8/schema.json +72 -0
  23. data/conf/0.8/schema.txt +57 -0
  24. data/conf/1.0/cassandra.in.sh +41 -0
  25. data/conf/1.0/cassandra.yaml +415 -0
  26. data/conf/1.0/log4j-server.properties +40 -0
  27. data/conf/1.0/schema.json +72 -0
  28. data/conf/1.0/schema.txt +57 -0
  29. data/conf/1.1/cassandra.in.sh +41 -0
  30. data/conf/1.1/cassandra.yaml +567 -0
  31. data/conf/1.1/log4j-server.properties +44 -0
  32. data/conf/1.1/schema.json +72 -0
  33. data/conf/1.1/schema.txt +57 -0
  34. data/conf/1.2/cassandra.in.sh +41 -0
  35. data/conf/1.2/cassandra.yaml +643 -0
  36. data/conf/1.2/log4j-server.properties +44 -0
  37. data/conf/1.2/schema.json +72 -0
  38. data/conf/1.2/schema.txt +57 -0
  39. data/ext/cassandra_native.c +69 -0
  40. data/ext/extconf.rb +9 -0
  41. data/lib/cassandra.rb +47 -0
  42. data/lib/cassandra/0.6.rb +7 -0
  43. data/lib/cassandra/0.6/cassandra.rb +113 -0
  44. data/lib/cassandra/0.6/columns.rb +78 -0
  45. data/lib/cassandra/0.6/protocol.rb +91 -0
  46. data/lib/cassandra/0.7.rb +7 -0
  47. data/lib/cassandra/0.7/cassandra.rb +2 -0
  48. data/lib/cassandra/0.7/columns.rb +4 -0
  49. data/lib/cassandra/0.7/protocol.rb +5 -0
  50. data/lib/cassandra/0.8.rb +7 -0
  51. data/lib/cassandra/0.8/cassandra.rb +25 -0
  52. data/lib/cassandra/0.8/columns.rb +28 -0
  53. data/lib/cassandra/0.8/protocol.rb +10 -0
  54. data/lib/cassandra/1.0.rb +7 -0
  55. data/lib/cassandra/1.0/cassandra.rb +1 -0
  56. data/lib/cassandra/1.0/columns.rb +1 -0
  57. data/lib/cassandra/1.0/protocol.rb +1 -0
  58. data/lib/cassandra/1.1.rb +7 -0
  59. data/lib/cassandra/1.1/cassandra.rb +1 -0
  60. data/lib/cassandra/1.1/columns.rb +1 -0
  61. data/lib/cassandra/1.1/protocol.rb +1 -0
  62. data/lib/cassandra/1.2.rb +7 -0
  63. data/lib/cassandra/1.2/cassandra.rb +1 -0
  64. data/lib/cassandra/1.2/columns.rb +1 -0
  65. data/lib/cassandra/1.2/protocol.rb +1 -0
  66. data/lib/cassandra/array.rb +8 -0
  67. data/lib/cassandra/batch.rb +41 -0
  68. data/lib/cassandra/cassandra.rb +1091 -0
  69. data/lib/cassandra/column_family.rb +3 -0
  70. data/lib/cassandra/columns.rb +172 -0
  71. data/lib/cassandra/comparable.rb +28 -0
  72. data/lib/cassandra/composite.rb +137 -0
  73. data/lib/cassandra/constants.rb +11 -0
  74. data/lib/cassandra/debug.rb +9 -0
  75. data/lib/cassandra/dynamic_composite.rb +118 -0
  76. data/lib/cassandra/helpers.rb +41 -0
  77. data/lib/cassandra/keyspace.rb +3 -0
  78. data/lib/cassandra/long.rb +58 -0
  79. data/lib/cassandra/mock.rb +536 -0
  80. data/lib/cassandra/ordered_hash.rb +195 -0
  81. data/lib/cassandra/protocol.rb +137 -0
  82. data/lib/cassandra/time.rb +11 -0
  83. data/test/cassandra_client_test.rb +20 -0
  84. data/test/cassandra_mock_test.rb +128 -0
  85. data/test/cassandra_test.rb +1367 -0
  86. data/test/comparable_types_test.rb +45 -0
  87. data/test/composite_type_test.rb +86 -0
  88. data/test/eventmachine_test.rb +42 -0
  89. data/test/ordered_hash_test.rb +386 -0
  90. data/test/test_helper.rb +19 -0
  91. data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
  92. data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
  93. data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
  94. data/vendor/0.7/gen-rb/cassandra.rb +1936 -0
  95. data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
  96. data/vendor/0.7/gen-rb/cassandra_types.rb +681 -0
  97. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  98. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  99. data/vendor/0.8/gen-rb/cassandra_types.rb +824 -0
  100. data/vendor/1.0/gen-rb/cassandra.rb +2215 -0
  101. data/vendor/1.0/gen-rb/cassandra_constants.rb +12 -0
  102. data/vendor/1.0/gen-rb/cassandra_types.rb +857 -0
  103. data/vendor/1.1/gen-rb/cassandra.rb +2571 -0
  104. data/vendor/1.1/gen-rb/cassandra_constants.rb +12 -0
  105. data/vendor/1.1/gen-rb/cassandra_types.rb +928 -0
  106. data/vendor/1.2/gen-rb/cassandra.rb +3013 -0
  107. data/vendor/1.2/gen-rb/cassandra_constants.rb +13 -0
  108. data/vendor/1.2/gen-rb/cassandra_types.rb +965 -0
  109. metadata +288 -0
@@ -0,0 +1,195 @@
1
+ # OrderedHash is namespaced to prevent conflicts with other implementations
2
+ class Cassandra
3
+ class OrderedHashInt < Hash #:nodoc:
4
+ def initialize(*args, &block)
5
+ super
6
+ @keys = []
7
+ end
8
+
9
+ def self.[](*args)
10
+ ordered_hash = new
11
+
12
+ if (args.length == 1 && args.first.is_a?(Array))
13
+ args.first.each do |key_value_pair|
14
+ next unless (key_value_pair.is_a?(Array))
15
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
16
+ end
17
+
18
+ return ordered_hash
19
+ end
20
+
21
+ unless (args.size % 2 == 0)
22
+ raise ArgumentError.new("odd number of arguments for Hash")
23
+ end
24
+
25
+ args.each_with_index do |val, ind|
26
+ next if (ind % 2 != 0)
27
+ ordered_hash[val] = args[ind + 1]
28
+ end
29
+
30
+ ordered_hash
31
+ end
32
+
33
+ def initialize_copy(other)
34
+ super
35
+ # make a deep copy of keys
36
+ @keys = other.keys
37
+ end
38
+
39
+ def []=(key, value)
40
+ @keys << key if !has_key?(key)
41
+ super
42
+ end
43
+
44
+ def delete(key)
45
+ if has_key? key
46
+ index = @keys.index(key)
47
+ @keys.delete_at index
48
+ end
49
+ super
50
+ end
51
+
52
+ def delete_if
53
+ super
54
+ sync_keys!
55
+ self
56
+ end
57
+
58
+ def reject!
59
+ super
60
+ sync_keys!
61
+ self
62
+ end
63
+
64
+ def reject(&block)
65
+ dup.reject!(&block)
66
+ end
67
+
68
+ def keys
69
+ @keys.dup
70
+ end
71
+
72
+ def values
73
+ @keys.collect { |key| self[key] }
74
+ end
75
+
76
+ def to_hash
77
+ self
78
+ end
79
+
80
+ def to_a
81
+ @keys.map { |key| [ key, self[key] ] }
82
+ end
83
+
84
+ def each_key
85
+ @keys.each { |key| yield key }
86
+ self
87
+ end
88
+
89
+ def each_value
90
+ @keys.each { |key| yield self[key]}
91
+ self
92
+ end
93
+
94
+ def each
95
+ @keys.each {|key| yield [key, self[key]]}
96
+ self
97
+ end
98
+
99
+ alias_method :each_pair, :each
100
+
101
+ def clear
102
+ super
103
+ @keys.clear
104
+ self
105
+ end
106
+
107
+ def shift
108
+ k = @keys.first
109
+ v = delete(k)
110
+ [k, v]
111
+ end
112
+
113
+ def merge!(other_hash)
114
+ other_hash.each {|k,v| self[k] = v }
115
+ self
116
+ end
117
+
118
+ def merge(other_hash)
119
+ dup.merge!(other_hash)
120
+ end
121
+
122
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
123
+ def replace(other)
124
+ super
125
+ @keys = other.keys
126
+ self
127
+ end
128
+
129
+ def reverse
130
+ OrderedHashInt[self.to_a.reverse]
131
+ end
132
+
133
+ private
134
+
135
+ def sync_keys!
136
+ @keys.delete_if {|k| !has_key?(k)}
137
+ end
138
+ end
139
+
140
+ class OrderedHash < OrderedHashInt #:nodoc:
141
+ def initialize(*args, &block)
142
+ @timestamps = OrderedHashInt.new
143
+ super
144
+ end
145
+
146
+ def initialize_copy(other)
147
+ @timestamps = other.timestamps
148
+ super
149
+ end
150
+
151
+ def []=(key, value, timestamp = nil)
152
+ @timestamps[key] = timestamp
153
+ super(key, value)
154
+ end
155
+
156
+ def delete(key)
157
+ @timestamps.delete(key)
158
+ super
159
+ end
160
+
161
+ def delete_if(&block)
162
+ @timestamps.delete_if(&block)
163
+ super
164
+ end
165
+
166
+ def reject!(&block)
167
+ @timestamps.reject!(&block)
168
+ super
169
+ end
170
+
171
+ def timestamps
172
+ @timestamps.dup
173
+ end
174
+
175
+ def clear
176
+ @timestamps.clear
177
+ super
178
+ end
179
+
180
+ def shift
181
+ k, v = super
182
+ @timestamps.delete(k)
183
+ [k, v]
184
+ end
185
+
186
+ def replace(other)
187
+ @timestamps = other.timestamps
188
+ super
189
+ end
190
+
191
+ def inspect
192
+ "#<OrderedHash #{super}\n#{@timestamps.inspect}>"
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,137 @@
1
+ class Cassandra
2
+ # Inner methods for actually doing the Thrift calls
3
+ module Protocol #:nodoc:
4
+ private
5
+
6
+ def _mutate(mutation_map, consistency_level)
7
+ client.batch_mutate(mutation_map, consistency_level)
8
+ end
9
+
10
+ def _remove(key, column_path, timestamp, consistency_level)
11
+ client.remove(key, column_path, timestamp, consistency_level)
12
+ end
13
+
14
+ def _count_columns(column_family, key, super_column, start, stop, count, consistency)
15
+ client.get_count(key,
16
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
17
+ CassandraThrift::SlicePredicate.new(:slice_range =>
18
+ CassandraThrift::SliceRange.new(
19
+ :start => start || '',
20
+ :finish => stop || '',
21
+ :count => count || 100
22
+ )),
23
+ consistency
24
+ )
25
+ end
26
+
27
+ # FIXME: Add support for start, stop, count
28
+ def _get_columns(column_family, key, columns, sub_columns, consistency)
29
+ result = if is_super(column_family)
30
+ if sub_columns
31
+ columns_to_hash(column_family, client.get_slice(key,
32
+ CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
33
+ CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
34
+ consistency))
35
+ else
36
+ columns_to_hash(column_family, client.get_slice(key,
37
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
38
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
39
+ consistency))
40
+ end
41
+ else
42
+ columns_to_hash(column_family, client.get_slice(key,
43
+ CassandraThrift::ColumnParent.new(:column_family => column_family),
44
+ CassandraThrift::SlicePredicate.new(:column_names => columns),
45
+ consistency))
46
+ end
47
+
48
+ klass = column_name_class(column_family)
49
+ (sub_columns || columns).map { |name| result[klass.new(name)] }
50
+ end
51
+
52
+ def _multi_get_columns(column_family, keys, columns, sub_columns, consistency)
53
+ result = if is_super(column_family) and sub_columns
54
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => sub_columns)
55
+ column_parent = CassandraThrift::ColumnParent.new(
56
+ :column_family => column_family,
57
+ :super_column => columns.kind_of?(Array) ? columns[0] : columns )
58
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
59
+ else
60
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => columns)
61
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
62
+ multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
63
+ end
64
+
65
+ klass = column_name_class(column_family)
66
+ OrderedHash[result.keys.map { |key| [key, (sub_columns || columns).map { |column| result[key][klass.new(column)] }] }]
67
+ end
68
+
69
+ def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
70
+ # Single values; count and range parameters have no effect
71
+ if is_super(column_family) and sub_column
72
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [sub_column])
73
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
74
+ column_hash = multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
75
+
76
+ klass = sub_column_name_class(column_family)
77
+ keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(sub_column)]; hash}
78
+ elsif !is_super(column_family) and column
79
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
80
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
81
+ column_hash = multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
82
+
83
+ klass = column_name_class(column_family)
84
+ keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(column)]; hash}
85
+
86
+ # Slices
87
+ else
88
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
89
+ CassandraThrift::SliceRange.new(
90
+ :reversed => reversed,
91
+ :count => count,
92
+ :start => start,
93
+ :finish => finish))
94
+
95
+ if is_super(column_family) and column
96
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
97
+ multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
98
+ else
99
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
100
+ multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
101
+ end
102
+ end
103
+ end
104
+
105
+ def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency, reversed=false)
106
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
107
+ predicate = if columns
108
+ CassandraThrift::SlicePredicate.new(:column_names => columns)
109
+ else
110
+ CassandraThrift::SlicePredicate.new(:slice_range =>
111
+ CassandraThrift::SliceRange.new(
112
+ :start => start,
113
+ :finish => finish,
114
+ :count => count,
115
+ :reversed => reversed))
116
+ end
117
+ range = CassandraThrift::KeyRange.new(:start_key => start_key, :end_key => finish_key, :count => key_count)
118
+ client.get_range_slices(column_parent, predicate, range, consistency)
119
+ end
120
+
121
+ # TODO: Supercolumn support
122
+ def _get_indexed_slices(column_family, index_clause, column, count, start, finish, reversed, consistency)
123
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
124
+ if column
125
+ predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
126
+ else
127
+ predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
128
+ CassandraThrift::SliceRange.new(
129
+ :reversed => reversed,
130
+ :count => count,
131
+ :start => start,
132
+ :finish => finish))
133
+ end
134
+ client.get_indexed_slices(column_parent, index_clause, predicate, consistency)
135
+ end
136
+ end
137
+ 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,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