redstruct 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -11
  3. data/Rakefile +5 -5
  4. data/lib/redstruct/all.rb +14 -0
  5. data/lib/redstruct/configuration.rb +9 -6
  6. data/lib/redstruct/connection_proxy.rb +123 -0
  7. data/lib/redstruct/counter.rb +96 -0
  8. data/lib/redstruct/error.rb +2 -0
  9. data/lib/redstruct/factory/object.rb +31 -0
  10. data/lib/redstruct/factory.rb +94 -55
  11. data/lib/redstruct/hash.rb +123 -0
  12. data/lib/redstruct/list.rb +315 -0
  13. data/lib/redstruct/lock.rb +183 -0
  14. data/lib/redstruct/script.rb +104 -0
  15. data/lib/redstruct/set.rb +155 -0
  16. data/lib/redstruct/sorted_set/slice.rb +124 -0
  17. data/lib/redstruct/sorted_set.rb +153 -0
  18. data/lib/redstruct/string.rb +66 -0
  19. data/lib/redstruct/struct.rb +87 -0
  20. data/lib/redstruct/utils/coercion.rb +14 -8
  21. data/lib/redstruct/utils/inspectable.rb +8 -4
  22. data/lib/redstruct/utils/iterable.rb +52 -0
  23. data/lib/redstruct/utils/scriptable.rb +32 -6
  24. data/lib/redstruct/version.rb +4 -1
  25. data/lib/redstruct.rb +17 -51
  26. data/lib/yard/defscript_handler.rb +5 -3
  27. data/test/redstruct/configuration_test.rb +13 -0
  28. data/test/redstruct/connection_proxy_test.rb +85 -0
  29. data/test/redstruct/counter_test.rb +108 -0
  30. data/test/redstruct/factory/object_test.rb +21 -0
  31. data/test/redstruct/factory_test.rb +136 -0
  32. data/test/redstruct/hash_test.rb +138 -0
  33. data/test/redstruct/list_test.rb +244 -0
  34. data/test/redstruct/lock_test.rb +108 -0
  35. data/test/redstruct/script_test.rb +53 -0
  36. data/test/redstruct/set_test.rb +219 -0
  37. data/test/redstruct/sorted_set/slice_test.rb +10 -0
  38. data/test/redstruct/sorted_set_test.rb +219 -0
  39. data/test/redstruct/string_test.rb +8 -0
  40. data/test/redstruct/struct_test.rb +61 -0
  41. data/test/redstruct/utils/coercion_test.rb +33 -0
  42. data/test/redstruct/utils/inspectable_test.rb +31 -0
  43. data/test/redstruct/utils/iterable_test.rb +94 -0
  44. data/test/redstruct/utils/scriptable_test.rb +67 -0
  45. data/test/redstruct_test.rb +14 -0
  46. data/test/test_helper.rb +77 -1
  47. metadata +58 -26
  48. data/lib/redstruct/connection.rb +0 -47
  49. data/lib/redstruct/factory/creation.rb +0 -95
  50. data/lib/redstruct/factory/deserialization.rb +0 -7
  51. data/lib/redstruct/hls/lock.rb +0 -175
  52. data/lib/redstruct/hls/queue.rb +0 -29
  53. data/lib/redstruct/hls.rb +0 -2
  54. data/lib/redstruct/types/base.rb +0 -36
  55. data/lib/redstruct/types/counter.rb +0 -65
  56. data/lib/redstruct/types/hash.rb +0 -72
  57. data/lib/redstruct/types/list.rb +0 -76
  58. data/lib/redstruct/types/script.rb +0 -56
  59. data/lib/redstruct/types/set.rb +0 -96
  60. data/lib/redstruct/types/sorted_set.rb +0 -129
  61. data/lib/redstruct/types/string.rb +0 -64
  62. data/lib/redstruct/types/struct.rb +0 -58
  63. data/lib/releaser/logger.rb +0 -15
  64. data/lib/releaser/repository.rb +0 -32
  65. data/lib/tasks/release.rake +0 -49
  66. data/test/redstruct/restruct_test.rb +0 -4
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ class SetTest < Redstruct::Test
7
+ def setup
8
+ super
9
+ @factory = create_factory
10
+ @set = @factory.set('set')
11
+ end
12
+
13
+ def test_clear
14
+ @set << 'item'
15
+ refute @set.empty?, 'ensure it is not empty before clearing'
16
+
17
+ @set.clear
18
+ assert @set.empty?, 'should be empty after clearing'
19
+ end
20
+
21
+ # assumes srandmember works correctly about the randomization part
22
+ def test_random
23
+ ensure_command_called(@set, :srandmember, 1).twice
24
+ assert_nil @set.random, 'should return nothing when the set is empty'
25
+
26
+ @set << 'a'
27
+ assert_equal 'a', @set.random, 'should return a, as it is the only member anyway'
28
+
29
+ amount = rand(10) + 2
30
+ requested = amount / 2
31
+ @set.add(*(1..amount).map { |i| i })
32
+
33
+ ensure_command_called(@set, :srandmember, requested).once
34
+ assert_equal requested, @set.random(count: requested).size, 'should return the amount of items requested'
35
+ end
36
+
37
+ def test_empty?
38
+ assert @set.empty?, 'should be initially empty'
39
+ @set << 'a'
40
+ refute @set.empty?, 'should not be empty once it has one element'
41
+ @set.clear
42
+ assert @set.empty?, 'should be empty after clearing'
43
+ end
44
+
45
+ def test_contain?
46
+ refute @set.contain?('a'), 'should not contain anything'
47
+ @set << 'a'
48
+ assert @set.contain?('a'), 'should now contain a'
49
+ end
50
+
51
+ def test_add
52
+ assert @set.empty?, 'set should be empty before any addition'
53
+ assert_equal 2, @set.add(1, 2), 'should return the number of added elements'
54
+ assert_equal 1, @set.add(1, 2, 3), 'should return only the number of added elements'
55
+ assert_equal ::Set.new(%w[1 2 3]), @set.to_set, 'should return a set containing 1, 2, 3'
56
+ end
57
+
58
+ def test_pop
59
+ values = %w[1 2 3]
60
+ assert @set.empty?, 'should start empty'
61
+
62
+ @set.add(*values)
63
+ popped = @set.pop
64
+ assert values.include?(popped), 'should have popped one of the added values'
65
+
66
+ expected = values - [popped]
67
+ assert_equal ::Set.new(expected), @set.to_set, 'should return a set containing the remaining elements'
68
+ end
69
+
70
+ def test_remove
71
+ values = %w[1 2 3]
72
+ assert @set.empty?, 'should start empty'
73
+
74
+ @set.add(*values)
75
+ assert @set.remove('1'), 'should remove the element correctly'
76
+ assert_equal ::Set.new(%w[2 3]), @set.to_set, 'should return the remaining elements'
77
+
78
+ assert_equal 2, @set.remove(2, 3, 4), 'should remove 2 elements only'
79
+ assert @set.empty?, 'should be empty once we remove everything'
80
+ end
81
+
82
+ def test_size
83
+ values = %w[1 2 3]
84
+ assert_equal 0, @set.size, 'should have no elements initially'
85
+
86
+ @set.add(*values)
87
+ assert_equal values.size, @set.size, 'should return the correct number of elements'
88
+ end
89
+
90
+ def test_difference
91
+ set_contents = %w[1 2 3 4]
92
+ set2_contents = %w[3 4 5 6]
93
+
94
+ set2 = @factory.set('set2')
95
+
96
+ @set.add(*set_contents)
97
+ set2.add(*set2_contents)
98
+
99
+ assert_equal ::Set.new(%w[1 2]), @set - set2, 'should return elements not contained in set2'
100
+ assert_equal ::Set.new(%w[5 6]), set2 - @set, 'should return elements not contained in set'
101
+
102
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
103
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
104
+ end
105
+
106
+ def test_difference_dest
107
+ set_contents = %w[1 2 3 4]
108
+ set2_contents = %w[3 4 5 6]
109
+
110
+ set2 = @factory.set('set2')
111
+ set3 = @factory.set('set3')
112
+
113
+ @set.add(*set_contents)
114
+ set2.add(*set2_contents)
115
+
116
+ assert_equal 2, @set.difference(set2, dest: set3), 'should have 2 elements stored in the new set'
117
+ assert_equal ::Set.new(%w[1 2]), set3.to_set, 'should return elements not contained in set2'
118
+
119
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
120
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
121
+ end
122
+
123
+ def test_intersection
124
+ set_contents = %w[1 2 3 4]
125
+ set2_contents = %w[3 4 5 6]
126
+
127
+ set2 = @factory.set('set2')
128
+
129
+ @set.add(*set_contents)
130
+ set2.add(*set2_contents)
131
+
132
+ assert_equal ::Set.new(%w[3 4]), @set | set2, 'should return elements contained in both sets'
133
+ assert_equal ::Set.new(%w[3 4]), set2 | @set, 'should return elements contained in both sets'
134
+
135
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
136
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
137
+ end
138
+
139
+ def test_intersection_dest
140
+ set_contents = %w[1 2 3 4]
141
+ set2_contents = %w[3 4 5 6]
142
+
143
+ set2 = @factory.set('set2')
144
+ set3 = @factory.set('set3')
145
+
146
+ @set.add(*set_contents)
147
+ set2.add(*set2_contents)
148
+
149
+ assert_equal 2, @set.intersection(set2, dest: set3), 'should have 2 elements stored in the new set'
150
+ assert_equal ::Set.new(%w[3 4]), set3.to_set, 'should return elements contained in both sets'
151
+
152
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
153
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
154
+ end
155
+
156
+ def test_union
157
+ set_contents = %w[1 2 3 4]
158
+ set2_contents = %w[3 4 5 6]
159
+
160
+ set2 = @factory.set('set2')
161
+
162
+ @set.add(*set_contents)
163
+ set2.add(*set2_contents)
164
+
165
+ assert_equal ::Set.new(%w[1 2 3 4 5 6]), @set + set2, 'should return elements contained in either sets'
166
+ assert_equal ::Set.new(%w[1 2 3 4 5 6]), set2 + @set, 'should return elements contained in either sets'
167
+
168
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
169
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
170
+ end
171
+
172
+ def test_union_dest
173
+ set_contents = %w[1 2 3 4]
174
+ set2_contents = %w[3 4 5 6]
175
+
176
+ set2 = @factory.set('set2')
177
+ set3 = @factory.set('set3')
178
+
179
+ @set.add(*set_contents)
180
+ set2.add(*set2_contents)
181
+
182
+ assert_equal 6, @set.union(set2, dest: set3), 'should have 6 elements stored in the new set'
183
+ assert_equal ::Set.new(%w[1 2 3 4 5 6]), set3.to_set, 'should return elements contained in either sets'
184
+
185
+ assert_equal ::Set.new(set_contents), @set.to_set, 'should still be the same'
186
+ assert_equal ::Set.new(set2_contents), set2.to_set, 'should still be the same'
187
+ end
188
+
189
+ def test_to_a
190
+ values = %w[1 2 3]
191
+ @set.add(*values)
192
+
193
+ assert_equal @set.to_a.sort, values, 'should return an array containing the correct values'
194
+ end
195
+
196
+ def test_to_set
197
+ values = %w[1 2 3]
198
+ @set.add(*values)
199
+
200
+ assert_equal ::Set.new(values), @set.to_set, 'should return the correct set'
201
+ end
202
+
203
+ def test_each
204
+ values = %w[a b c d aa]
205
+ @set.add(*values)
206
+
207
+ missing = values.dup
208
+ @set.each do |word|
209
+ assert values.include?(word), 'should be in the values'
210
+ missing.delete(word)
211
+ end
212
+ assert_empty missing, 'should not have missed anything'
213
+
214
+ matched = ::Set.new
215
+ @set.each(match: 'a*') { |word| matched << word }
216
+ assert_equal ::Set.new(%w[a aa]), matched, 'should contain a and aa only'
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ class SortedSet
7
+ class SliceTest < Redstruct::Test
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ class SortedTest < Redstruct::Test
7
+ def setup
8
+ super
9
+ @factory = create_factory
10
+ @set = @factory.sorted_set('zset')
11
+ end
12
+
13
+ def test_initialize
14
+ refute @set.lexicographic?, 'should not be a lexicographically sorted set'
15
+
16
+ lex = @factory.sorted_set('lzset', lex: true)
17
+ assert lex.lexicographic?, 'should be marked as a lexicographically sorted set'
18
+ end
19
+
20
+ def test_clear
21
+ assert @set.empty?, 'should initially be empty'
22
+ @set.add(1, 'a')
23
+ refute @set.empty?, 'should not be empty'
24
+
25
+ assert @set.clear, 'should have been cleared'
26
+ assert @set.empty?, 'should now be empty again'
27
+ end
28
+
29
+ def test_empty?
30
+ assert @set.empty?, 'should be empty initially'
31
+ @set.add(1, 'a')
32
+ refute @set.empty?, 'should now not be empty'
33
+ end
34
+
35
+ def test_add
36
+ values = { 1 => 'a', 2 => 'c', 3 => 'b' }
37
+ assert_equal 3, @set.add(*values.keys.zip(values.values)), 'should have added 3 items'
38
+ assert_equal 0, @set.add(1, 'a'), 'should not add pre-existing value'
39
+ assert_equal ::Set.new(%w[a b c]), @set.to_set, 'should contain exactly the values added'
40
+ end
41
+
42
+ def test_add_exists
43
+ assert_equal 0, @set.add([1, 'a'], exists: true), 'should not have added anything items'
44
+ assert @set.empty?, 'should not contain anything'
45
+
46
+ @set.add([2, 'a'])
47
+ assert_equal 2.0, @set.score('a'), 'should have score of 2'
48
+ assert_equal 1, @set.add([1, 'a'], exists: true), 'should have added 1 element'
49
+ assert_equal 1.0, @set.score('a'), 'should have score of 1'
50
+ end
51
+
52
+ def test_add_overwrite
53
+ @set.add([2, 'b'])
54
+ assert_equal 0, @set.add([3, 'b'], overwrite: false), 'should not overwrite pre-existing value'
55
+ assert_equal 2.0, @set.score('b'), 'should still have old score'
56
+ assert_equal 1, @set.add([3, 'b'], overwrite: true), 'should have overwritten pre-existing value'
57
+ assert_equal 3.0, @set.score('b'), 'should now have score of 3'
58
+ end
59
+
60
+ def test_add_lex
61
+ set = @factory.sorted_set('zset', lex: true)
62
+ assert_equal 2, set.add([1, 'c'], [2, 'd']), 'should have added 2 elements'
63
+ assert_equal 2, set.add('a', 'b'), 'should have added 2 elements'
64
+
65
+ assert_equal %w[a b c d], set.to_a, 'should be lexicographically sorted'
66
+ %w[a b c d].each do |letter|
67
+ assert_equal 0.0, set.score(letter), 'should have a score of 0'
68
+ end
69
+ end
70
+
71
+ def test_increment
72
+ assert_equal 1.0, @set.increment('a'), 'should have a default score of 1.0'
73
+ assert_equal 3.0, @set.increment('a', by: 2.0), 'should have incremented the score by 2'
74
+ assert_equal 3.0, @set.score('a'), 'should have the correct score of 3.0'
75
+ end
76
+
77
+ def test_increment_lex
78
+ set = @factory.sorted_set('zset', lex: true)
79
+ assert_raises(NotImplementedError, 'should not be able to increment scores for lexicographic sets') do
80
+ set.increment('a')
81
+ end
82
+ end
83
+
84
+ def test_decrement
85
+ assert_equal(-1.0, @set.decrement('a'), 'should have a default score of -1.0')
86
+ assert_equal(-3.0, @set.decrement('a', by: 2.0), 'should have decremented the score by 2')
87
+ assert_equal(-3.0, @set.score('a'), 'should have the correct score of -3.0')
88
+ end
89
+
90
+ def test_decrement_lex
91
+ set = @factory.sorted_set('zset', lex: true)
92
+ assert_raises(NotImplementedError, 'should not be able to decrement scores for lexicographic sets') do
93
+ set.decrement('a')
94
+ end
95
+ end
96
+
97
+ def test_size
98
+ assert_equal 0, @set.size, 'should have a size of 0 initially'
99
+ @set.add([1, 'a'], [2, 'b'], [3, 'c'])
100
+ assert_equal 3, @set.size, 'should have a size of 3'
101
+ end
102
+
103
+ # The slice structure has its own tests, so we only test the creation of
104
+ # a slice here.
105
+ def test_slice
106
+ slice = @set.slice
107
+ assert_equal @set.key, slice.key, 'should have the same object key'
108
+ assert_equal @set.factory, slice.factory, 'should have the same initial factory'
109
+ assert_equal '-inf', slice.lower, 'should have infinity as lower bound'
110
+ assert_equal '+inf', slice.upper, 'should have infinity as upper bound'
111
+ refute slice.exclusive, 'should not be exclusive by default'
112
+
113
+ slice = @set.slice(lower: 1, upper: 3, exclusive: true)
114
+ assert_equal '(1.0', slice.lower, 'should have exclusive 1.0 as lower bound'
115
+ assert_equal '(3.0', slice.upper, 'should have exclusive 3.0 as upper bound'
116
+
117
+ slice = @set.slice(lower: 1, upper: 3, exclusive: false)
118
+ assert_equal 1.0, slice.lower, 'should have inclusive 1.0 as lower bound'
119
+ assert_equal 3.0, slice.upper, 'should have inclusive 3.0 as upper bound'
120
+ end
121
+
122
+ def test_slice_lex
123
+ set = @factory.sorted_set('zset', lex: true)
124
+ slice = set.slice
125
+ assert slice.lex, 'should be a lexicographical slice'
126
+ assert_equal '-', slice.lower, 'should have lex infinity as lower bound'
127
+ assert_equal '+', slice.upper, 'should have lex infinity as upper bound'
128
+
129
+ slice = set.slice(lower: 'a', upper: 'd', exclusive: true)
130
+ assert_equal '(a', slice.lower, 'should have exclusive lower bound a'
131
+ assert_equal '(d', slice.upper, 'should have exclusive upper bound d'
132
+
133
+ slice = set.slice(lower: 'a', upper: 'd', exclusive: false)
134
+ assert_equal '[a', slice.lower, 'should have inclusive lower bound a'
135
+ assert_equal '[d', slice.upper, 'should have inclusive upper bound d'
136
+ end
137
+
138
+ def test_contain?
139
+ refute @set.contain?('a'), 'should not contain a initially'
140
+ @set.add([1, 'a'])
141
+ assert @set.contain?('a'), 'should now contain a'
142
+ end
143
+
144
+ def test_index
145
+ assert_nil @set.index('a'), 'should not return any index initially'
146
+ @set.add([2, 'b'], [1, 'a'])
147
+ assert_equal 0, @set.index('a'), 'should return correct index for a'
148
+ assert_equal 1, @set.index('b'), 'should return correct index for b'
149
+ end
150
+
151
+ def test_rindex
152
+ assert_nil @set.rindex('a'), 'should not return any index initially'
153
+ @set.add([2, 'b'], [1, 'a'])
154
+ assert_equal 1, @set.rindex('a'), 'should return correct index for a'
155
+ assert_equal 0, @set.rindex('b'), 'should return correct index for b'
156
+ end
157
+
158
+ def test_score
159
+ assert_nil @set.score('a'), 'should return nil when item not in the set'
160
+ @set.add([1, 'a'])
161
+ assert_equal 1.0, @set.score('a'), 'should return the correct score'
162
+ end
163
+
164
+ def test_remove
165
+ items = %w[a b c]
166
+ assert_equal 0, @set.remove(*items), 'should not have removed anything'
167
+
168
+ @set.add(*[1, 2, 3].zip(items))
169
+ @set.add([4, 'd'])
170
+ assert_equal 1, @set.remove('d', 'e'), 'should only remove 1 element'
171
+ assert_equal items, @set.to_a, 'should contain the correct elements'
172
+ assert_equal 3, @set.remove(*items), 'should have removed all 3 elements'
173
+ assert @set.empty?, 'should be empty now'
174
+ end
175
+
176
+ def test_to_a
177
+ assert_empty @set.to_a, 'should initially return an empty array'
178
+ @set.add([4, 'a'], [3, 'b'], [2, 'c'], [1, 'd'])
179
+ assert_equal %w[d c b a], @set.to_a, 'should return a sorted array'
180
+ end
181
+
182
+ def test_to_set
183
+ items = %w[a b]
184
+ assert_equal ::Set.new, @set.to_set, 'should return an empty set'
185
+ @set.add(*[1, 2].zip(items))
186
+ assert_equal ::Set.new(items), @set.to_set, 'should return a set containing the elements'
187
+ end
188
+
189
+ def test_each
190
+ values = %w[aa b c d a]
191
+ scores = %w[1 2 3 4 5]
192
+ sorted = scores.zip(values).sort_by { |pair| pair[0].to_i }
193
+ @set.add(*sorted)
194
+
195
+ received = []
196
+ @set.each do |word|
197
+ assert values.include?(word), 'should be in the values'
198
+ received << word
199
+ end
200
+ assert_equal received, @set.to_a, 'should have received it all in order'
201
+
202
+ received = []
203
+ @set.each(match: 'a*') { |word| received << word }
204
+ assert_equal %w[aa a], received, 'should contain a and aa only'
205
+ end
206
+
207
+ def test_each_with_scores
208
+ values = %w[aa b c d a]
209
+ scores = %w[1 2 3 4 5].map(&:to_f)
210
+ sorted = scores.zip(values).sort_by { |pair| pair[0].to_i }
211
+ @set.add(*sorted)
212
+
213
+ @set.each(with_scores: true).each_with_index do |(value, score), index|
214
+ assert_equal values[index], value, 'should receive the correct value'
215
+ assert_equal scores[index], score, 'should receive the correct score'
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ class StringTest < Redstruct::Test
7
+ end
8
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'test_helper'
5
+
6
+ module Redstruct
7
+ class StructTest < Redstruct::Test
8
+ def setup
9
+ super
10
+ @factory = create_factory
11
+ @struct = @factory.struct('struct')
12
+ end
13
+
14
+ def test_initialize
15
+ key = @factory.prefix(SecureRandom.hex(4))
16
+ struct = Redstruct::Struct.new(key: key, factory: @factory)
17
+ assert_equal key, struct.key, 'should have key unchanged after initialization'
18
+ end
19
+
20
+ def test_exists?
21
+ refute @struct.exists?, 'struct should not yet exist'
22
+ write_struct
23
+ assert @struct.exists?, 'struct should exist if underlying redis key exists'
24
+ end
25
+
26
+ def test_delete
27
+ refute @struct.delete, 'should return false since the struct did not exist'
28
+ write_struct
29
+ assert @struct.delete, 'should return true since a key was actually deleted'
30
+ end
31
+
32
+ def test_expire
33
+ refute @struct.expire(1), 'should return false since no existing key was expired'
34
+ write_struct
35
+ assert @struct.expire(1), 'should have correctly expired the existing key'
36
+ end
37
+
38
+ def test_expire_at
39
+ refute @struct.expire_at(1), 'should return false since no existing key was expired'
40
+ write_struct
41
+ assert @struct.expire_at(1), 'should have correctly marked to key to be expired'
42
+ refute @struct.exists?, 'should not exist since it was marked to be expired 1 second after 1970-01-01'
43
+ end
44
+
45
+ def test_persist; end
46
+
47
+ def test_type; end
48
+
49
+ def test_ttl; end
50
+
51
+ def test_dump; end
52
+
53
+ def test_restore; end
54
+
55
+ # a struct has no set value, so use the redis connection to "cheat" and set one so the struct actually exists
56
+ def write_struct
57
+ @struct.connection.set(@struct.key, 'foo')
58
+ end
59
+ private :write_struct
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ module Utils
7
+ class CoercionTest < Redstruct::Test
8
+ def test_coerce_bool
9
+ refute Redstruct::Utils::Coercion.coerce_bool(nil), 'nil should be coerced to false'
10
+ refute Redstruct::Utils::Coercion.coerce_bool(false), 'false should be coerced to false'
11
+ refute Redstruct::Utils::Coercion.coerce_bool(0), '0 should be coerced to false'
12
+ refute Redstruct::Utils::Coercion.coerce_bool(0.0), '0.0 should be coerced to false'
13
+ assert Redstruct::Utils::Coercion.coerce_bool(1), 'any non-zero number should be coerced to true'
14
+
15
+ [[], {}, '', true, 3.0, Object.new].each do |value|
16
+ assert Redstruct::Utils::Coercion.coerce_bool(value), 'should be coerced to true'
17
+ end
18
+ end
19
+
20
+ def test_coerce_array
21
+ assert_equal [], Redstruct::Utils::Coercion.coerce_array(nil), 'nil should be coerced to empty array'
22
+
23
+ array = [1, 2, 3]
24
+ assert_equal array, Redstruct::Utils::Coercion.coerce_array(array), 'array should be returned as is'
25
+
26
+ hash = { a: 1, b: 2 }
27
+ assert_equal hash.to_a, Redstruct::Utils::Coercion.coerce_array(hash), '#to_a should be coerced to its array representation'
28
+
29
+ assert_equal [1], Redstruct::Utils::Coercion.coerce_array(1), 'non #to_a should be coerced to an array containing the given value'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'flexmock/minitest'
5
+
6
+ module Redstruct
7
+ module Utils
8
+ class InspectableTest < Redstruct::Test
9
+ def test_inspect
10
+ child = flexmock('test')
11
+ child.should_receive(:inspect).and_return('child').once
12
+ other = 'val'
13
+
14
+ object = self.class::Test.new(child: child, other: other)
15
+ assert_equal %(Redstruct::Utils::InspectableTest::Test: child: <child>, other: <"val">), object.inspect, 'should generate the correct inspect string, calling inspect on all values'
16
+ end
17
+
18
+ class Test
19
+ include Redstruct::Utils::Inspectable
20
+
21
+ def initialize(attrs)
22
+ @attrs = attrs
23
+ end
24
+
25
+ def inspectable_attributes
26
+ return @attrs
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Redstruct
6
+ module Utils
7
+ class IterableTest < Redstruct::Test
8
+ def test_to_enum_bad_impl
9
+ assert_raises(NotImplementedError, 'should fail and raise not implemented on missing implementations') do
10
+ BadIterator.new.to_enum
11
+ end
12
+ end
13
+
14
+ def test_each_bad_impl
15
+ assert_raises(NotImplementedError, 'should fail and raise not implemented on missing implementations') do
16
+ BadIterator.new.each { |o| o }
17
+ end
18
+ end
19
+
20
+ def test_each_no_block
21
+ iterator = self.class::Iterator.new
22
+ enum = iterator.each # returns an enum
23
+
24
+ assert_kind_of Enumerator, enum, 'should return an enumerator when called with no block'
25
+ assert_equal 0, iterator.iterations, 'should not actually have been called ever yet'
26
+ end
27
+
28
+ def test_each_block
29
+ iterator = self.class::Iterator.new
30
+ ones = []
31
+
32
+ iterator.each(max_iterations: 2) { |one| ones << one }
33
+ assert_equal [1, 1], ones, 'should have returned an array of 2 "1"s'
34
+ end
35
+
36
+ # each does not do anything with match or count but pass them on to to_enum, so make sure that this is what we do
37
+ def test_each_match_count
38
+ iterator = self.class::Iterator.new
39
+ _ = iterator.each(match: 'match', count: 1) # returns an enum
40
+
41
+ assert_equal 'match', iterator.match, 'should have received the correct match param'
42
+ assert_equal 1, iterator.count, 'should have received the correct count param'
43
+ end
44
+
45
+ def test_each_max_iterations
46
+ iterator = self.class::Iterator.new
47
+ enum = iterator.each(max_iterations: 10)
48
+
49
+ assert_equal 10, enum.to_a.size, 'should iterate up to 10 times, therefore containing 10 "1"s in the array'
50
+ assert_equal 10, iterator.iterations, 'should have iterated exactly 10 times'
51
+ end
52
+
53
+ def test_each_batch_size
54
+ iterator = self.class::Iterator.new
55
+ iterator.each(max_iterations: 2, batch_size: 2).each do |ones|
56
+ assert_equal 2, ones.size, 'should yield in batches of 2 elements'
57
+ assert_equal [1, 1], ones, 'should yield a list of 2 "1"s'
58
+ end
59
+ end
60
+
61
+ # Sample class not implementing interface
62
+ class BadIterator
63
+ include Redstruct::Utils::Iterable
64
+ end
65
+
66
+ class Iterator
67
+ include Redstruct::Utils::Iterable
68
+
69
+ attr_reader :iterations, :match, :count
70
+
71
+ def initialize
72
+ @iterations = 0
73
+ @match = nil
74
+ @count = nil
75
+ end
76
+
77
+ # the idea is to return an infinite stream of 1s, but monitor how the enum was constructed, and how many
78
+ # iterations were performed, as opposed to creating a mock object
79
+ def to_enum(match: '*', count: 10)
80
+ @match = match
81
+ @count = count
82
+ @iterations = 0
83
+
84
+ return Enumerator.new do |yielder|
85
+ loop do
86
+ @iterations += 1 # increment before yielding, since yielding might immediately raise StopIteration, but we'll still have iterated once
87
+ yielder << 1
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end