redstruct 0.1.7 → 0.2.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 (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