berater 0.4.0 → 0.5.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.
@@ -16,6 +16,12 @@ describe 'be_overloaded' do
16
16
  it { expect { limiter.limit }.not_to be_inhibited }
17
17
  it { expect { limiter.limit }.not_to be_overrated }
18
18
  it { expect { limiter.limit }.not_to be_incapacitated }
19
+
20
+ it 'catches false positives' do
21
+ expect {
22
+ expect { limiter }.to be_overloaded
23
+ }.to fail
24
+ end
19
25
  end
20
26
 
21
27
  context 'Berater::Inhibitor' do
@@ -29,10 +35,16 @@ describe 'be_overloaded' do
29
35
 
30
36
  it { expect { limiter.limit }.to be_overloaded }
31
37
  it { expect { limiter.limit }.to be_inhibited }
38
+
39
+ it 'catches false negatives' do
40
+ expect {
41
+ expect { limiter }.not_to be_overloaded
42
+ }.to fail
43
+ end
32
44
  end
33
45
 
34
46
  context 'Berater::RateLimiter' do
35
- let(:limiter) { Berater.new(:key, :rate, 1, :second) }
47
+ let(:limiter) { Berater.new(:key, 1, :second) }
36
48
 
37
49
  it { expect(limiter).not_to be_overloaded }
38
50
  it { expect(limiter).not_to be_inhibited }
@@ -67,7 +79,7 @@ describe 'be_overloaded' do
67
79
  end
68
80
 
69
81
  context 'Berater::ConcurrencyLimiter' do
70
- let(:limiter) { Berater.new(:key, :concurrency, 1) }
82
+ let(:limiter) { Berater.new(:key, 1) }
71
83
 
72
84
  it { expect(limiter).not_to be_overloaded }
73
85
  it { expect(limiter).not_to be_inhibited }
@@ -1,12 +1,12 @@
1
1
  describe Berater::RateLimiter do
2
- it_behaves_like 'a limiter', Berater.new(:key, :rate, 3, :second)
2
+ it_behaves_like 'a limiter', Berater.new(:key, 3, :second)
3
3
 
4
4
  describe '.new' do
5
5
  let(:limiter) { described_class.new(:key, 1, :second) }
6
6
 
7
7
  it 'initializes' do
8
8
  expect(limiter.key).to be :key
9
- expect(limiter.count).to eq 1
9
+ expect(limiter.capacity).to eq 1
10
10
  expect(limiter.interval).to eq :second
11
11
  end
12
12
 
@@ -15,82 +15,38 @@ describe Berater::RateLimiter do
15
15
  end
16
16
  end
17
17
 
18
- describe '#count' do
19
- def expect_count(count)
20
- limiter = described_class.new(:key, count, :second)
21
- expect(limiter.count).to eq count
18
+ describe '#capacity' do
19
+ def expect_capacity(capacity)
20
+ limiter = described_class.new(:key, capacity, :second)
21
+ expect(limiter.capacity).to eq capacity
22
22
  end
23
23
 
24
- it { expect_count(0) }
25
- it { expect_count(1) }
26
- it { expect_count(100) }
24
+ it { expect_capacity(0) }
25
+ it { expect_capacity(1) }
26
+ it { expect_capacity(100) }
27
27
 
28
28
  context 'with erroneous values' do
29
- def expect_bad_count(count)
29
+ def expect_bad_capacity(capacity)
30
30
  expect do
31
- described_class.new(:key, count, :second)
31
+ described_class.new(:key, capacity, :second)
32
32
  end.to raise_error ArgumentError
33
33
  end
34
34
 
35
- it { expect_bad_count(0.5) }
36
- it { expect_bad_count(-1) }
37
- it { expect_bad_count('1') }
38
- it { expect_bad_count(:one) }
35
+ it { expect_bad_capacity(0.5) }
36
+ it { expect_bad_capacity(-1) }
37
+ it { expect_bad_capacity('1') }
38
+ it { expect_bad_capacity(:one) }
39
39
  end
40
40
  end
41
41
 
42
42
  describe '#interval' do
43
- def expect_interval(interval, expected)
44
- limiter = described_class.new(:key, 1, interval)
45
- expect(limiter.interval).to eq expected
46
- end
47
-
48
- context 'with ints' do
49
- it { expect_interval(0, 0) }
50
- it { expect_interval(1, 1) }
51
- it { expect_interval(33, 33) }
52
- end
53
-
54
- context 'with symbols' do
55
- it { expect_interval(:sec, :second) }
56
- it { expect_interval(:second, :second) }
57
- it { expect_interval(:seconds, :second) }
58
-
59
- it { expect_interval(:min, :minute) }
60
- it { expect_interval(:minute, :minute) }
61
- it { expect_interval(:minutes, :minute) }
62
-
63
- it { expect_interval(:hour, :hour) }
64
- it { expect_interval(:hours, :hour) }
65
- end
66
-
67
- context 'with strings' do
68
- it { expect_interval('sec', :second) }
69
- it { expect_interval('minute', :minute) }
70
- it { expect_interval('hours', :hour) }
71
- end
72
-
73
- context 'with erroneous values' do
74
- def expect_bad_interval(interval)
75
- expect do
76
- described_class.new(:key, 1, interval)
77
- end.to raise_error(ArgumentError)
78
- end
79
-
80
- it { expect_bad_interval(-1) }
81
- it { expect_bad_interval(:secondz) }
82
- it { expect_bad_interval('huor') }
83
- end
43
+ # see spec/utils_spec.rb for more
84
44
 
85
- context 'interprets values' do
86
- def expect_sec(interval, expected)
87
- limiter = described_class.new(:key, 1, interval)
88
- expect(limiter.instance_variable_get(:@interval_sec)).to eq expected
89
- end
45
+ subject { described_class.new(:key, 1, :second) }
90
46
 
91
- it { expect_sec(:second, 1) }
92
- it { expect_sec(:minute, 60) }
93
- it { expect_sec(:hour, 3600) }
47
+ it 'saves the interval in original and microsecond format' do
48
+ expect(subject.interval).to be :second
49
+ expect(subject.instance_variable_get(:@interval_usec)).to be 10**6
94
50
  end
95
51
  end
96
52
 
@@ -112,50 +68,101 @@ describe Berater::RateLimiter do
112
68
  expect(limiter).to be_overrated
113
69
  end
114
70
 
115
- it 'limit resets over time' do
71
+ it 'limit resets over time, with millisecond precision' do
116
72
  3.times { limiter.limit }
117
73
  expect(limiter).to be_overrated
118
74
 
119
- # travel forward a second
75
+ # travel forward to just before the count decrements
76
+ Timecop.freeze(0.333)
77
+ expect(limiter).to be_overrated
78
+
79
+ # traveling one more millisecond will decrement the count
80
+ Timecop.freeze(0.001)
81
+ limiter.limit
82
+ expect(limiter).to be_overrated
83
+
84
+ # traveling 1 second will reset the count
120
85
  Timecop.freeze(1)
121
86
 
122
87
  3.times { limiter.limit }
123
88
  expect(limiter).to be_overrated
124
89
  end
125
- end
126
90
 
127
- context 'with same key, different limiters' do
128
- let(:limiter_one) { described_class.new(:key, 1, :second) }
129
- let(:limiter_two) { described_class.new(:key, 1, :second) }
91
+ it 'accepts a dynamic capacity' do
92
+ limiter = described_class.new(:key, 1, :second)
130
93
 
131
- it 'works as expected' do
132
- expect(limiter_one.limit).not_to be_overrated
94
+ expect { limiter.limit(capacity: 0) }.to be_overrated
95
+ 5.times { limiter.limit(capacity: 10) }
96
+ expect { limiter }.to be_overrated
97
+ end
98
+
99
+ context 'works with cost parameter' do
100
+ it { expect { limiter.limit(cost: 4) }.to be_overrated }
133
101
 
134
- expect(limiter_one).to be_overrated
135
- expect(limiter_two).to be_overrated
102
+ it 'works within limit' do
103
+ limiter.limit(cost: 3)
104
+ expect { limiter.limit }.to be_overrated
105
+ end
106
+
107
+ it 'resets over time' do
108
+ limiter.limit(cost: 3)
109
+ expect(limiter).to be_overrated
110
+
111
+ Timecop.freeze(1)
112
+ expect(limiter).not_to be_overrated
113
+ end
114
+
115
+ it 'can be a Float' do
116
+ 2.times { limiter.limit(cost: 1.5) }
117
+ expect(limiter).to be_overrated
118
+ end
136
119
  end
137
- end
138
120
 
139
- context 'with different keys, different limiters' do
140
- let(:limiter_one) { described_class.new(:one, 1, :second) }
141
- let(:limiter_two) { described_class.new(:two, 2, :second) }
121
+ context 'with same key, different limiters' do
122
+ let(:limiter_one) { described_class.new(:key, 1, :second) }
123
+ let(:limiter_two) { described_class.new(:key, 1, :second) }
142
124
 
143
- it 'works as expected' do
144
- expect(limiter_one.limit).not_to be_overrated
145
- expect(limiter_two.limit).not_to be_overrated
125
+ it 'works as expected' do
126
+ expect(limiter_one.limit).not_to be_overrated
146
127
 
147
- expect(limiter_one).to be_overrated
148
- expect(limiter_two.limit).not_to be_overrated
128
+ expect(limiter_one).to be_overrated
129
+ expect(limiter_two).to be_overrated
130
+ end
131
+ end
132
+
133
+ context 'with different keys, different limiters' do
134
+ let(:limiter_one) { described_class.new(:one, 1, :second) }
135
+ let(:limiter_two) { described_class.new(:two, 2, :second) }
149
136
 
150
- expect(limiter_one).to be_overrated
151
- expect(limiter_two).to be_overrated
137
+ it 'works as expected' do
138
+ expect(limiter_one.limit).not_to be_overrated
139
+ expect(limiter_two.limit).not_to be_overrated
140
+
141
+ expect(limiter_one).to be_overrated
142
+ expect(limiter_two.limit).not_to be_overrated
143
+
144
+ expect(limiter_one).to be_overrated
145
+ expect(limiter_two).to be_overrated
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#overloaded?' do
151
+ let(:limiter) { described_class.new(:key, 1, :second) }
152
+
153
+ it 'works' do
154
+ expect(limiter.overloaded?).to be false
155
+ limiter.limit
156
+ expect(limiter.overloaded?).to be true
157
+ Timecop.freeze(1)
158
+ expect(limiter.overloaded?).to be false
152
159
  end
153
160
  end
154
161
 
155
162
  describe '#to_s' do
156
- def check(count, interval, expected)
163
+ def check(capacity, interval, expected)
157
164
  expect(
158
- described_class.new(:key, count, interval).to_s
165
+ described_class.new(:key, capacity, interval).to_s
159
166
  ).to match(expected)
160
167
  end
161
168
 
@@ -171,16 +178,6 @@ describe Berater::RateLimiter do
171
178
  check(1, 'hour', /1 per hour/)
172
179
  end
173
180
 
174
- it 'normalizes' do
175
- check(1, :sec, /1 per second/)
176
- check(1, :seconds, /1 per second/)
177
-
178
- check(1, :min, /1 per minute/)
179
- check(1, :minutes, /1 per minute/)
180
-
181
- check(1, :hours, /1 per hour/)
182
- end
183
-
184
181
  it 'works with integers' do
185
182
  check(1, 1, /1 every second/)
186
183
  check(1, 2, /1 every 2 seconds/)
@@ -0,0 +1,102 @@
1
+ # Can you build a rate limiter from a concurrency limiter? Yes!
2
+
3
+ class RateRiddler
4
+ def self.limit(capacity, interval)
5
+ lock = Berater::ConcurrencyLimiter.new(:key, capacity, timeout: interval).limit
6
+ yield if block_given?
7
+ # allow lock to time out rather than be released
8
+ end
9
+ end
10
+
11
+
12
+ describe 'a ConcurrencyLimiter-derived rate limiter' do
13
+ def limit(&block)
14
+ RateRiddler.limit(1, :second, &block)
15
+ end
16
+
17
+ it 'works' do
18
+ expect(limit { 123 }).to eq 123
19
+ end
20
+
21
+ it 'respects limits' do
22
+ limit
23
+ expect { limit }.to be_overloaded
24
+ end
25
+
26
+ it 'resets over time' do
27
+ limit
28
+ expect { limit }.to be_overloaded
29
+
30
+ Timecop.freeze(1)
31
+
32
+ limit
33
+ expect { limit }.to be_overloaded
34
+ end
35
+ end
36
+
37
+
38
+ # Can you build a concurrency limiter from a rate limiter? Almost...
39
+
40
+ class ConcurrenyRiddler
41
+ def self.limit(capacity, timeout: nil)
42
+ timeout ||= 1_000 # fake infinity
43
+
44
+ limiter = Berater::RateLimiter.new(:key, capacity, timeout)
45
+ limiter.limit
46
+ yield if block_given?
47
+ ensure
48
+ # decrement counter
49
+ limiter.redis.decr(limiter.send(:cache_key, :key))
50
+ end
51
+ end
52
+
53
+
54
+ describe 'a RateLimiter-derived concurrency limiter' do
55
+ def limit(capacity = 1, timeout: nil, &block)
56
+ ConcurrenyRiddler.limit(capacity, timeout: timeout, &block)
57
+ end
58
+
59
+ it 'works' do
60
+ expect(limit { 123 }).to eq 123
61
+ end
62
+
63
+ it 'respects limits' do
64
+ limit do
65
+ # a second, simultaneous request isn't allowed
66
+ expect { limit }.to be_overloaded
67
+ end
68
+
69
+ # but now it'll work
70
+ limit
71
+ end
72
+
73
+ it 'resets over time' do
74
+ limit(timeout: 1) do
75
+ expect { limit }.to be_overloaded
76
+
77
+ # ...wait for it
78
+ Timecop.freeze(10)
79
+
80
+ limit(timeout: 1)
81
+ end
82
+ end
83
+
84
+ it "has no memory of the order, so timeouts don't work quite right" do
85
+ limit(2, timeout: 1) do
86
+ Timecop.freeze(0.5)
87
+
88
+ limit(2, timeout: 1) do
89
+ # this is where the masquerading breaks. the first lock is still
90
+ # being held and within it's timeout limit, however the RaterLimiter
91
+ # decremented the count internally since enough time has passed.
92
+ # This next call *should* fail, but doesn't.
93
+
94
+ expect {
95
+ expect { limit(2, timeout: 1) }.to be_overloaded
96
+ }.to fail
97
+
98
+ # ...close, but not quite!
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,84 +1,138 @@
1
- require 'berater/test_mode'
1
+ describe Berater::TestMode, order: :defined do
2
+ let(:reset_test_mode) { true }
2
3
 
3
- describe 'Berater.test_mode' do
4
- after { Berater.test_mode = nil }
4
+ after do
5
+ Berater.test_mode = nil if reset_test_mode
6
+ end
7
+
8
+ context 'after test_mode.rb was required, but not used' do
9
+ let(:reset_test_mode) { false }
10
+
11
+ it 'has already been loaded by "berater/rspec", unfortunately' do
12
+ expect {
13
+ expect { Berater.test_mode }.to raise_error(NoMethodError)
14
+ }.to fail
15
+ end
16
+
17
+ it 'defaults to off' do
18
+ expect(Berater.test_mode).to be nil
19
+ end
20
+
21
+ it 'did not prepend .new yet' do
22
+ expect(Berater::Limiter.singleton_class.ancestors).not_to include(described_class)
23
+ end
24
+
25
+ it 'prepends when first turned on' do
26
+ Berater.test_mode = :pass
27
+
28
+ expect(Berater::Limiter.singleton_class.ancestors).to include(described_class)
29
+ end
30
+
31
+ it 'preserves the original functionality via super' do
32
+ expect { Berater::Limiter.new }.to raise_error(NotImplementedError)
33
+ end
34
+ end
35
+
36
+ describe '.test_mode' do
37
+ it 'can be turned on' do
38
+ Berater.test_mode = :pass
39
+ expect(Berater.test_mode).to be :pass
40
+
41
+ Berater.test_mode = :fail
42
+ expect(Berater.test_mode).to be :fail
43
+ end
44
+
45
+ it 'can be turned off' do
46
+ Berater.test_mode = nil
47
+ expect(Berater.test_mode).to be nil
48
+ end
49
+
50
+ it 'validates input' do
51
+ expect { Berater.test_mode = :foo }.to raise_error(ArgumentError)
52
+ end
53
+ end
54
+
55
+ shared_examples 'it always works, without redis' do
56
+ before do
57
+ Berater.redis = nil
58
+ expect_any_instance_of(Berater::LuaScript).not_to receive(:eval)
59
+ end
60
+
61
+ it_behaves_like 'it is not overloaded'
62
+
63
+ it 'always works' do
64
+ 10.times { subject.limit }
65
+ end
66
+ end
67
+
68
+ shared_examples 'it never works, without redis' do
69
+ before do
70
+ Berater.redis = nil
71
+ expect_any_instance_of(Berater::LuaScript).not_to receive(:eval)
72
+ end
73
+
74
+ it_behaves_like 'it is overloaded'
75
+
76
+ it 'never works' do
77
+ expect { subject }.to be_overloaded
78
+ end
79
+ end
5
80
 
6
81
  describe 'Unlimiter' do
7
- let(:limiter) { Berater::Unlimiter.new }
82
+ subject { Berater::Unlimiter.new }
8
83
 
9
84
  context 'when test_mode = nil' do
10
85
  before { Berater.test_mode = nil }
11
86
 
12
- it { expect(limiter).to be_a Berater::Unlimiter }
13
-
14
- it 'works per usual' do
15
- expect {|block| limiter.limit(&block) }.to yield_control
16
- 10.times { expect(limiter.limit).to be_a Berater::Lock }
17
- end
87
+ it { is_expected.to be_a Berater::Unlimiter }
88
+ it_behaves_like 'it always works, without redis'
18
89
  end
19
90
 
20
91
  context 'when test_mode = :pass' do
21
92
  before { Berater.test_mode = :pass }
22
93
 
23
- it { expect(limiter).to be_a Berater::Unlimiter }
24
-
25
- it 'always works' do
26
- expect {|block| limiter.limit(&block) }.to yield_control
27
- 10.times { expect(limiter.limit).to be_a Berater::Lock }
28
- end
94
+ it { is_expected.to be_a Berater::Unlimiter }
95
+ it_behaves_like 'it always works, without redis'
29
96
  end
30
97
 
31
98
  context 'when test_mode = :fail' do
32
99
  before { Berater.test_mode = :fail }
33
100
 
34
- it { expect(limiter).to be_a Berater::Unlimiter }
35
-
36
- it 'never works' do
37
- expect { limiter }.to be_overloaded
38
- end
101
+ it { is_expected.to be_a Berater::Unlimiter }
102
+ it_behaves_like 'it never works, without redis'
39
103
  end
40
104
  end
41
105
 
42
106
  describe 'Inhibitor' do
43
- let(:limiter) { Berater::Inhibitor.new }
107
+ subject { Berater::Inhibitor.new }
44
108
 
45
109
  context 'when test_mode = nil' do
46
110
  before { Berater.test_mode = nil }
47
111
 
48
- it { expect(limiter).to be_a Berater::Inhibitor }
49
-
50
- it 'works per usual' do
51
- expect { limiter }.to be_overloaded
52
- end
112
+ it { is_expected.to be_a Berater::Inhibitor }
113
+ it_behaves_like 'it never works, without redis'
53
114
  end
54
115
 
55
116
  context 'when test_mode = :pass' do
56
117
  before { Berater.test_mode = :pass }
57
118
 
58
- it { expect(limiter).to be_a Berater::Inhibitor }
59
-
60
- it 'always works' do
61
- expect {|block| limiter.limit(&block) }.to yield_control
62
- 10.times { expect(limiter.limit).to be_a Berater::Lock }
63
- end
119
+ it { is_expected.to be_a Berater::Inhibitor }
120
+ it_behaves_like 'it always works, without redis'
64
121
  end
65
122
 
66
123
  context 'when test_mode = :fail' do
67
124
  before { Berater.test_mode = :fail }
68
125
 
69
- it { expect(limiter).to be_a Berater::Inhibitor }
70
-
71
- it 'never works' do
72
- expect { limiter }.to be_overloaded
73
- end
126
+ it { is_expected.to be_a Berater::Inhibitor }
127
+ it_behaves_like 'it never works, without redis'
74
128
  end
75
129
  end
76
130
 
77
131
  describe 'RateLimiter' do
78
- let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
132
+ subject { Berater::RateLimiter.new(:key, 1, :second) }
79
133
 
80
134
  shared_examples 'a RateLimiter' do
81
- it { expect(limiter).to be_a Berater::RateLimiter }
135
+ it { is_expected.to be_a Berater::RateLimiter }
82
136
 
83
137
  it 'checks arguments' do
84
138
  expect {
@@ -91,15 +145,12 @@ describe 'Berater.test_mode' do
91
145
  before { Berater.test_mode = nil }
92
146
 
93
147
  it_behaves_like 'a RateLimiter'
148
+ it_behaves_like 'it is not overloaded'
94
149
 
95
150
  it 'works per usual' do
96
- expect(limiter.redis).to receive(:eval).twice.and_call_original
97
- expect(limiter.limit).to be_a Berater::Lock
98
- expect { limiter.limit }.to be_overloaded
99
- end
100
-
101
- it 'yields per usual' do
102
- expect {|block| limiter.limit(&block) }.to yield_control
151
+ expect(Berater::RateLimiter::LUA_SCRIPT).to receive(:eval).twice.and_call_original
152
+ expect(subject.limit).to be_a Berater::Lock
153
+ expect { subject.limit }.to be_overloaded
103
154
  end
104
155
  end
105
156
 
@@ -107,31 +158,22 @@ describe 'Berater.test_mode' do
107
158
  before { Berater.test_mode = :pass }
108
159
 
109
160
  it_behaves_like 'a RateLimiter'
110
-
111
- it 'always works and without calling redis' do
112
- expect(limiter.redis).not_to receive(:eval)
113
- expect {|block| limiter.limit(&block) }.to yield_control
114
- 10.times { expect(limiter.limit).to be_a Berater::Lock }
115
- end
161
+ it_behaves_like 'it always works, without redis'
116
162
  end
117
163
 
118
164
  context 'when test_mode = :fail' do
119
165
  before { Berater.test_mode = :fail }
120
166
 
121
167
  it_behaves_like 'a RateLimiter'
122
-
123
- it 'never works and without calling redis' do
124
- expect(limiter.redis).not_to receive(:eval)
125
- expect { limiter }.to be_overloaded
126
- end
168
+ it_behaves_like 'it never works, without redis'
127
169
  end
128
170
  end
129
171
 
130
172
  describe 'ConcurrencyLimiter' do
131
- let(:limiter) { Berater::ConcurrencyLimiter.new(:key, 1) }
173
+ subject { Berater::ConcurrencyLimiter.new(:key, 1) }
132
174
 
133
175
  shared_examples 'a ConcurrencyLimiter' do
134
- it { expect(limiter).to be_a Berater::ConcurrencyLimiter }
176
+ it { expect(subject).to be_a Berater::ConcurrencyLimiter }
135
177
 
136
178
  it 'checks arguments' do
137
179
  expect {
@@ -144,15 +186,12 @@ describe 'Berater.test_mode' do
144
186
  before { Berater.test_mode = nil }
145
187
 
146
188
  it_behaves_like 'a ConcurrencyLimiter'
189
+ it_behaves_like 'it is not overloaded'
147
190
 
148
191
  it 'works per usual' do
149
- expect(limiter.redis).to receive(:eval).twice.and_call_original
150
- expect(limiter.limit).to be_a Berater::Lock
151
- expect { limiter.limit }.to be_overloaded
152
- end
153
-
154
- it 'yields per usual' do
155
- expect {|block| limiter.limit(&block) }.to yield_control
192
+ expect(Berater::ConcurrencyLimiter::LUA_SCRIPT).to receive(:eval).twice.and_call_original
193
+ expect(subject.limit).to be_a Berater::Lock
194
+ expect { subject.limit }.to be_overloaded
156
195
  end
157
196
  end
158
197
 
@@ -160,23 +199,14 @@ describe 'Berater.test_mode' do
160
199
  before { Berater.test_mode = :pass }
161
200
 
162
201
  it_behaves_like 'a ConcurrencyLimiter'
163
-
164
- it 'always works and without calling redis' do
165
- expect(limiter.redis).not_to receive(:eval)
166
- expect {|block| limiter.limit(&block) }.to yield_control
167
- 10.times { expect(limiter.limit).to be_a Berater::Lock }
168
- end
202
+ it_behaves_like 'it always works, without redis'
169
203
  end
170
204
 
171
205
  context 'when test_mode = :fail' do
172
206
  before { Berater.test_mode = :fail }
173
207
 
174
208
  it_behaves_like 'a ConcurrencyLimiter'
175
-
176
- it 'never works and without calling redis' do
177
- expect(limiter.redis).not_to receive(:eval)
178
- expect { limiter }.to be_overloaded
179
- end
209
+ it_behaves_like 'it never works, without redis'
180
210
  end
181
211
  end
182
212