berater 0.1.4 → 0.6.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.
- checksums.yaml +4 -4
- data/lib/berater.rb +33 -35
- data/lib/berater/concurrency_limiter.rb +68 -110
- data/lib/berater/dsl.rb +68 -0
- data/lib/berater/inhibitor.rb +9 -10
- data/lib/berater/limiter.rb +80 -0
- data/lib/berater/lock.rb +26 -0
- data/lib/berater/lua_script.rb +55 -0
- data/lib/berater/rate_limiter.rb +77 -54
- data/lib/berater/rspec.rb +14 -0
- data/lib/berater/rspec/matchers.rb +83 -0
- data/lib/berater/test_mode.rb +52 -0
- data/lib/berater/unlimiter.rb +11 -9
- data/lib/berater/utils.rb +46 -0
- data/lib/berater/version.rb +1 -1
- data/spec/berater_spec.rb +43 -101
- data/spec/concurrency_limiter_spec.rb +168 -100
- data/spec/dsl_refinement_spec.rb +46 -0
- data/spec/dsl_spec.rb +72 -0
- data/spec/inhibitor_spec.rb +4 -18
- data/spec/limiter_spec.rb +71 -0
- data/spec/lua_script_spec.rb +97 -0
- data/spec/{matcher_spec.rb → matchers_spec.rb} +73 -5
- data/spec/rate_limiter_spec.rb +162 -99
- data/spec/riddle_spec.rb +102 -0
- data/spec/test_mode_spec.rb +206 -0
- data/spec/unlimiter_spec.rb +6 -37
- data/spec/utils_spec.rb +78 -0
- metadata +41 -8
- data/lib/berater/base_limiter.rb +0 -32
- data/spec/concurrency_lock_spec.rb +0 -92
data/lib/berater/version.rb
CHANGED
data/spec/berater_spec.rb
CHANGED
@@ -7,12 +7,12 @@ describe Berater do
|
|
7
7
|
it { is_expected.to respond_to :configure }
|
8
8
|
|
9
9
|
describe '.configure' do
|
10
|
-
it '
|
10
|
+
it 'is used with a block' do
|
11
11
|
Berater.configure do |c|
|
12
|
-
c.
|
12
|
+
c.redis = :redis
|
13
13
|
end
|
14
14
|
|
15
|
-
expect(Berater.
|
15
|
+
expect(Berater.redis).to be :redis
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -24,20 +24,13 @@ describe Berater do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
describe '.
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
context 'unlimited mode' do
|
34
|
-
before { Berater.mode = :unlimited }
|
35
|
-
|
36
|
-
describe '.new' do
|
37
|
-
let(:limiter) { Berater.new(:unlimited) }
|
27
|
+
describe '.new' do
|
28
|
+
context 'unlimited mode' do
|
29
|
+
let(:limiter) { Berater.new(:key, Float::INFINITY) }
|
38
30
|
|
39
31
|
it 'instantiates an Unlimiter' do
|
40
32
|
expect(limiter).to be_a Berater::Unlimiter
|
33
|
+
expect(limiter.key).to be :key
|
41
34
|
end
|
42
35
|
|
43
36
|
it 'inherits redis' do
|
@@ -46,35 +39,17 @@ describe Berater do
|
|
46
39
|
|
47
40
|
it 'accepts options' do
|
48
41
|
redis = double('Redis')
|
49
|
-
limiter = Berater.new(:
|
50
|
-
expect(limiter.key).to match(/key/)
|
42
|
+
limiter = Berater.new(:key, Float::INFINITY, redis: redis)
|
51
43
|
expect(limiter.redis).to be redis
|
52
44
|
end
|
53
45
|
end
|
54
46
|
|
55
|
-
|
56
|
-
|
57
|
-
expect(Berater.limit).to be_nil
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'yields' do
|
61
|
-
expect {|b| Berater.limit(&b) }.to yield_control
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'never limits' do
|
65
|
-
10.times { expect(Berater.limit { 123 } ).to eq 123 }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'inhibited mode' do
|
71
|
-
before { Berater.mode = :inhibited }
|
72
|
-
|
73
|
-
describe '.new' do
|
74
|
-
let(:limiter) { Berater.new(:inhibited) }
|
47
|
+
context 'inhibited mode' do
|
48
|
+
let(:limiter) { Berater.new(:key, 0) }
|
75
49
|
|
76
50
|
it 'instantiates an Inhibitor' do
|
77
51
|
expect(limiter).to be_a Berater::Inhibitor
|
52
|
+
expect(limiter.key).to be :key
|
78
53
|
end
|
79
54
|
|
80
55
|
it 'inherits redis' do
|
@@ -83,27 +58,17 @@ describe Berater do
|
|
83
58
|
|
84
59
|
it 'accepts options' do
|
85
60
|
redis = double('Redis')
|
86
|
-
limiter = Berater.new(:
|
87
|
-
expect(limiter.key).to match(/key/)
|
61
|
+
limiter = Berater.new(:key, 0, redis: redis)
|
88
62
|
expect(limiter.redis).to be redis
|
89
63
|
end
|
90
64
|
end
|
91
65
|
|
92
|
-
|
93
|
-
|
94
|
-
expect { Berater.limit }.to be_inhibited
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
context 'rate mode' do
|
100
|
-
before { Berater.mode = :rate }
|
101
|
-
|
102
|
-
describe '.limiter' do
|
103
|
-
let(:limiter) { Berater.new(:rate, 1, :second) }
|
66
|
+
context 'rate mode' do
|
67
|
+
let(:limiter) { Berater.new(:key, 1, :second) }
|
104
68
|
|
105
69
|
it 'instantiates a RateLimiter' do
|
106
70
|
expect(limiter).to be_a Berater::RateLimiter
|
71
|
+
expect(limiter.key).to be :key
|
107
72
|
end
|
108
73
|
|
109
74
|
it 'inherits redis' do
|
@@ -112,44 +77,17 @@ describe Berater do
|
|
112
77
|
|
113
78
|
it 'accepts options' do
|
114
79
|
redis = double('Redis')
|
115
|
-
limiter = Berater.new(:
|
116
|
-
expect(limiter.key).to match(/key/)
|
80
|
+
limiter = Berater.new(:key, 1, :second, redis: redis)
|
117
81
|
expect(limiter.redis).to be redis
|
118
82
|
end
|
119
83
|
end
|
120
84
|
|
121
|
-
|
122
|
-
|
123
|
-
expect(Berater.limit(1, :second)).to eq 1
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'yields' do
|
127
|
-
expect {|b| Berater.limit(2, :second, &b) }.to yield_control
|
128
|
-
expect(Berater.limit(2, :second) { 123 }).to eq 123
|
129
|
-
end
|
130
|
-
|
131
|
-
it 'limits excessive calls' do
|
132
|
-
expect(Berater.limit(1, :second)).to eq 1
|
133
|
-
expect { Berater.limit(1, :second) }.to be_overrated
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'accepts options' do
|
137
|
-
redis = double('Redis')
|
138
|
-
expect(redis).to receive(:multi)
|
139
|
-
|
140
|
-
Berater.limit(1, :second, redis: redis) rescue nil
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'concurrency mode' do
|
146
|
-
before { Berater.mode = :concurrency }
|
147
|
-
|
148
|
-
describe '.limiter' do
|
149
|
-
let(:limiter) { Berater.new(:concurrency, 1) }
|
85
|
+
context 'concurrency mode' do
|
86
|
+
let(:limiter) { Berater.new(:key, 1) }
|
150
87
|
|
151
88
|
it 'instantiates a ConcurrencyLimiter' do
|
152
89
|
expect(limiter).to be_a Berater::ConcurrencyLimiter
|
90
|
+
expect(limiter.key).to be :key
|
153
91
|
end
|
154
92
|
|
155
93
|
it 'inherits redis' do
|
@@ -158,35 +96,39 @@ describe Berater do
|
|
158
96
|
|
159
97
|
it 'accepts options' do
|
160
98
|
redis = double('Redis')
|
161
|
-
limiter = Berater.new(:
|
162
|
-
expect(limiter.key).to match(/key/)
|
99
|
+
limiter = Berater.new(:key, 1, redis: redis)
|
163
100
|
expect(limiter.redis).to be redis
|
164
101
|
end
|
165
102
|
end
|
103
|
+
end
|
166
104
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
expect(
|
172
|
-
end
|
173
|
-
|
174
|
-
it 'yields' do
|
175
|
-
expect {|b| Berater.limit(1, &b) }.to yield_control
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'limits excessive calls' do
|
179
|
-
Berater.limit(1)
|
180
|
-
expect { Berater.limit(1) }.to be_incapacitated
|
105
|
+
describe 'Berater() - convenience method' do
|
106
|
+
RSpec.shared_examples 'test convenience' do |klass, *args|
|
107
|
+
it 'creates a limiter' do
|
108
|
+
limiter = Berater(:key, *args)
|
109
|
+
expect(limiter).to be_a klass
|
181
110
|
end
|
182
111
|
|
183
|
-
|
184
|
-
|
185
|
-
|
112
|
+
context 'with a block' do
|
113
|
+
it 'creates a limiter and calls limit' do
|
114
|
+
limiter = Berater(:key, *args)
|
115
|
+
expect(klass).to receive(:new).and_return(limiter)
|
116
|
+
expect(limiter).to receive(:limit).and_call_original
|
186
117
|
|
187
|
-
|
118
|
+
begin
|
119
|
+
res = Berater(:key, *args) { true }
|
120
|
+
expect(res).to be true
|
121
|
+
rescue Berater::Overloaded
|
122
|
+
expect(klass).to be Berater::Inhibitor
|
123
|
+
end
|
124
|
+
end
|
188
125
|
end
|
189
126
|
end
|
127
|
+
|
128
|
+
include_examples 'test convenience', Berater::Unlimiter, Float::INFINITY
|
129
|
+
include_examples 'test convenience', Berater::Inhibitor, 0
|
130
|
+
include_examples 'test convenience', Berater::RateLimiter, 1, :second
|
131
|
+
include_examples 'test convenience', Berater::ConcurrencyLimiter, 1
|
190
132
|
end
|
191
133
|
|
192
134
|
end
|
@@ -1,37 +1,38 @@
|
|
1
1
|
describe Berater::ConcurrencyLimiter do
|
2
|
-
|
2
|
+
it_behaves_like 'a limiter', described_class.new(:key, 1)
|
3
|
+
it_behaves_like 'a limiter', described_class.new(:key, 1, timeout: 1)
|
3
4
|
|
4
5
|
describe '.new' do
|
5
|
-
let(:limiter) { described_class.new(1) }
|
6
|
+
let(:limiter) { described_class.new(:key, 1) }
|
6
7
|
|
7
8
|
it 'initializes' do
|
9
|
+
expect(limiter.key).to be :key
|
8
10
|
expect(limiter.capacity).to be 1
|
9
11
|
end
|
10
12
|
|
11
13
|
it 'has default values' do
|
12
|
-
expect(limiter.key).to eq described_class.to_s
|
13
14
|
expect(limiter.redis).to be Berater.redis
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
18
|
describe '#capacity' do
|
18
19
|
def expect_capacity(capacity)
|
19
|
-
limiter = described_class.new(capacity)
|
20
|
+
limiter = described_class.new(:key, capacity)
|
20
21
|
expect(limiter.capacity).to eq capacity
|
21
22
|
end
|
22
23
|
|
23
24
|
it { expect_capacity(0) }
|
24
25
|
it { expect_capacity(1) }
|
26
|
+
it { expect_capacity(1.5) }
|
25
27
|
it { expect_capacity(10_000) }
|
26
28
|
|
27
29
|
context 'with erroneous values' do
|
28
30
|
def expect_bad_capacity(capacity)
|
29
31
|
expect do
|
30
|
-
described_class.new(capacity)
|
32
|
+
described_class.new(:key, capacity)
|
31
33
|
end.to raise_error ArgumentError
|
32
34
|
end
|
33
35
|
|
34
|
-
it { expect_bad_capacity(0.5) }
|
35
36
|
it { expect_bad_capacity(-1) }
|
36
37
|
it { expect_bad_capacity('1') }
|
37
38
|
it { expect_bad_capacity(:one) }
|
@@ -39,31 +40,23 @@ describe Berater::ConcurrencyLimiter do
|
|
39
40
|
end
|
40
41
|
|
41
42
|
describe '#timeout' do
|
42
|
-
|
43
|
-
limiter = described_class.new(1, timeout: timeout)
|
44
|
-
expect(limiter.timeout).to eq timeout
|
45
|
-
end
|
46
|
-
|
47
|
-
it { expect_timeout(0) }
|
48
|
-
it { expect_timeout(1) }
|
49
|
-
it { expect_timeout(10_000) }
|
43
|
+
# see spec/utils_spec.rb
|
50
44
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
45
|
+
it 'saves the interval in original and millisecond format' do
|
46
|
+
limiter = described_class.new(:key, 1, timeout: 3)
|
47
|
+
expect(limiter.timeout).to be 3
|
48
|
+
expect(limiter.instance_variable_get(:@timeout_msec)).to be (3 * 10**3)
|
49
|
+
end
|
57
50
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
51
|
+
it 'handles infinity' do
|
52
|
+
limiter = described_class.new(:key, 1, timeout: Float::INFINITY)
|
53
|
+
expect(limiter.timeout).to be Float::INFINITY
|
54
|
+
expect(limiter.instance_variable_get(:@timeout_msec)).to be 0
|
62
55
|
end
|
63
56
|
end
|
64
57
|
|
65
58
|
describe '#limit' do
|
66
|
-
let(:limiter) { described_class.new(2, timeout:
|
59
|
+
let(:limiter) { described_class.new(:key, 2, timeout: 30) }
|
67
60
|
|
68
61
|
it 'works' do
|
69
62
|
expect {|b| limiter.limit(&b) }.to yield_control
|
@@ -81,121 +74,196 @@ describe Berater::ConcurrencyLimiter do
|
|
81
74
|
end
|
82
75
|
|
83
76
|
it 'limits excessive calls' do
|
84
|
-
expect(limiter.limit).to be_a Berater::
|
85
|
-
expect(limiter.limit).to be_a Berater::
|
77
|
+
expect(limiter.limit).to be_a Berater::Lock
|
78
|
+
expect(limiter.limit).to be_a Berater::Lock
|
79
|
+
|
80
|
+
expect(limiter).to be_incapacitated
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with capacity 0' do
|
84
|
+
let(:limiter) { described_class.new(:key, 0) }
|
85
|
+
|
86
|
+
it 'always fails' do
|
87
|
+
expect(limiter).to be_incapacitated
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when capacity is a Float' do
|
92
|
+
let(:limiter) { described_class.new(:key, 1.5) }
|
93
|
+
|
94
|
+
it 'still works' do
|
95
|
+
lock = limiter.limit
|
96
|
+
|
97
|
+
# since fractional cost is not supported
|
98
|
+
expect(lock.capacity).to be 1
|
99
|
+
expect(limiter).to be_incapacitated
|
100
|
+
end
|
101
|
+
end
|
86
102
|
|
103
|
+
it 'limit resets over time' do
|
104
|
+
2.times { limiter.limit }
|
105
|
+
expect(limiter).to be_incapacitated
|
106
|
+
|
107
|
+
Timecop.freeze(30)
|
108
|
+
|
109
|
+
2.times { limiter.limit }
|
87
110
|
expect(limiter).to be_incapacitated
|
88
111
|
end
|
89
112
|
|
90
|
-
it '
|
91
|
-
|
92
|
-
expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
|
113
|
+
it 'limit resets with millisecond precision' do
|
114
|
+
2.times { limiter.limit }
|
93
115
|
expect(limiter).to be_incapacitated
|
94
116
|
|
95
|
-
|
117
|
+
# travel forward to just before first lock times out
|
118
|
+
Timecop.freeze(29.999)
|
119
|
+
expect(limiter).to be_incapacitated
|
96
120
|
|
97
|
-
|
98
|
-
|
121
|
+
# traveling one more millisecond will decrement the count
|
122
|
+
Timecop.freeze(0.001)
|
123
|
+
2.times { limiter.limit }
|
99
124
|
expect(limiter).to be_incapacitated
|
100
125
|
end
|
101
126
|
|
102
|
-
context 'with
|
103
|
-
|
127
|
+
context 'with cost parameter' do
|
128
|
+
it { expect { limiter.limit(cost: 4) }.to be_incapacitated }
|
104
129
|
|
105
|
-
it '
|
130
|
+
it 'works within limit' do
|
131
|
+
limiter.limit(cost: 2)
|
106
132
|
expect(limiter).to be_incapacitated
|
107
133
|
end
|
108
|
-
end
|
109
|
-
end
|
110
134
|
|
111
|
-
|
112
|
-
|
113
|
-
|
135
|
+
it 'releases full cost' do
|
136
|
+
lock = limiter.limit(cost: 2)
|
137
|
+
expect(limiter).to be_incapacitated
|
138
|
+
|
139
|
+
lock.release
|
140
|
+
expect(limiter).not_to be_incapacitated
|
114
141
|
|
115
|
-
|
142
|
+
lock = limiter.limit(cost: 2)
|
143
|
+
expect(limiter).to be_incapacitated
|
144
|
+
end
|
116
145
|
|
117
|
-
|
118
|
-
|
146
|
+
it 'respects timeout' do
|
147
|
+
limiter.limit(cost: 2)
|
148
|
+
expect(limiter).to be_incapacitated
|
119
149
|
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
end
|
150
|
+
Timecop.freeze(30)
|
151
|
+
expect(limiter).not_to be_incapacitated
|
124
152
|
|
125
|
-
|
126
|
-
|
153
|
+
limiter.limit(cost: 2)
|
154
|
+
expect(limiter).to be_incapacitated
|
155
|
+
end
|
127
156
|
|
128
|
-
|
129
|
-
|
130
|
-
|
157
|
+
it 'accepts a dynamic capacity' do
|
158
|
+
limiter = described_class.new(:key, 1)
|
159
|
+
|
160
|
+
expect { limiter.limit(capacity: 0) }.to be_incapacitated
|
161
|
+
5.times { limiter.limit(capacity: 10) }
|
162
|
+
expect { limiter }.to be_incapacitated
|
163
|
+
end
|
131
164
|
|
132
|
-
|
133
|
-
|
165
|
+
it 'only allows positive, Integer values' do
|
166
|
+
expect { limiter.limit(cost: -1) }.to raise_error(ArgumentError)
|
167
|
+
expect { limiter.limit(cost: 1.5) }.to raise_error(ArgumentError)
|
168
|
+
end
|
169
|
+
end
|
134
170
|
|
135
|
-
|
136
|
-
|
171
|
+
context 'with same key, different limiters' do
|
172
|
+
let(:limiter_one) { described_class.new(:key, 1) }
|
173
|
+
let(:limiter_two) { described_class.new(:key, 1) }
|
137
174
|
|
138
|
-
|
139
|
-
expect { limiter.limit(key: :two) {} }.to be_incapacitated
|
175
|
+
it { expect(limiter_one.key).to eq limiter_two.key }
|
140
176
|
|
141
|
-
|
142
|
-
|
143
|
-
expect { limiter.limit(key: :two) {} }.to be_incapacitated
|
177
|
+
it 'works as expected' do
|
178
|
+
expect(limiter_one.limit).to be_a Berater::Lock
|
144
179
|
|
145
|
-
|
146
|
-
|
147
|
-
|
180
|
+
expect(limiter_one).to be_incapacitated
|
181
|
+
expect(limiter_two).to be_incapacitated
|
182
|
+
end
|
148
183
|
end
|
149
|
-
end
|
150
184
|
|
151
|
-
|
152
|
-
|
153
|
-
|
185
|
+
context 'with same key, different capacities' do
|
186
|
+
let(:limiter_one) { described_class.new(:key, 1) }
|
187
|
+
let(:limiter_two) { described_class.new(:key, 2) }
|
154
188
|
|
155
|
-
|
189
|
+
it { expect(limiter_one.capacity).not_to eq limiter_two.capacity }
|
156
190
|
|
157
|
-
|
158
|
-
|
159
|
-
|
191
|
+
it 'works as expected' do
|
192
|
+
one_lock = limiter_one.limit
|
193
|
+
expect(one_lock).to be_a Berater::Lock
|
160
194
|
|
161
|
-
|
162
|
-
|
195
|
+
expect(limiter_one).to be_incapacitated
|
196
|
+
expect(limiter_two).not_to be_incapacitated
|
163
197
|
|
164
|
-
|
165
|
-
|
198
|
+
two_lock = limiter_two.limit
|
199
|
+
expect(two_lock).to be_a Berater::Lock
|
166
200
|
|
167
|
-
|
168
|
-
|
201
|
+
expect(limiter_one).to be_incapacitated
|
202
|
+
expect(limiter_two).to be_incapacitated
|
169
203
|
|
170
|
-
|
171
|
-
|
172
|
-
|
204
|
+
one_lock.release
|
205
|
+
expect(limiter_one).to be_incapacitated
|
206
|
+
expect(limiter_two).not_to be_incapacitated
|
173
207
|
|
174
|
-
|
175
|
-
|
176
|
-
|
208
|
+
two_lock.release
|
209
|
+
expect(limiter_one).not_to be_incapacitated
|
210
|
+
expect(limiter_two).not_to be_incapacitated
|
211
|
+
end
|
177
212
|
end
|
178
|
-
end
|
179
213
|
|
180
|
-
|
181
|
-
|
182
|
-
|
214
|
+
context 'with different keys, different limiters' do
|
215
|
+
let(:limiter_one) { described_class.new(:one, 1) }
|
216
|
+
let(:limiter_two) { described_class.new(:two, 1) }
|
217
|
+
|
218
|
+
it 'works as expected' do
|
219
|
+
expect(limiter_one).not_to be_incapacitated
|
220
|
+
expect(limiter_two).not_to be_incapacitated
|
221
|
+
|
222
|
+
one_lock = limiter_one.limit
|
223
|
+
expect(one_lock).to be_a Berater::Lock
|
224
|
+
|
225
|
+
expect(limiter_one).to be_incapacitated
|
226
|
+
expect(limiter_two).not_to be_incapacitated
|
227
|
+
|
228
|
+
two_lock = limiter_two.limit
|
229
|
+
expect(two_lock).to be_a Berater::Lock
|
230
|
+
|
231
|
+
expect(limiter_one).to be_incapacitated
|
232
|
+
expect(limiter_two).to be_incapacitated
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
183
236
|
|
184
|
-
|
185
|
-
|
186
|
-
expect(limiter_two).not_to be_incapacitated
|
237
|
+
describe '#overloaded?' do
|
238
|
+
let(:limiter) { described_class.new(:key, 1, timeout: 30) }
|
187
239
|
|
188
|
-
|
189
|
-
expect(
|
240
|
+
it 'works' do
|
241
|
+
expect(limiter.overloaded?).to be false
|
242
|
+
lock = limiter.limit
|
243
|
+
expect(limiter.overloaded?).to be true
|
244
|
+
lock.release
|
245
|
+
expect(limiter.overloaded?).to be false
|
246
|
+
end
|
190
247
|
|
191
|
-
|
192
|
-
expect(
|
248
|
+
it 'respects timeout' do
|
249
|
+
expect(limiter.overloaded?).to be false
|
250
|
+
lock = limiter.limit
|
251
|
+
expect(limiter.overloaded?).to be true
|
252
|
+
Timecop.freeze(30)
|
253
|
+
expect(limiter.overloaded?).to be false
|
254
|
+
end
|
255
|
+
end
|
193
256
|
|
194
|
-
|
195
|
-
|
257
|
+
describe '#to_s' do
|
258
|
+
def check(capacity, expected)
|
259
|
+
expect(
|
260
|
+
described_class.new(:key, capacity).to_s
|
261
|
+
).to match(expected)
|
262
|
+
end
|
196
263
|
|
197
|
-
|
198
|
-
|
264
|
+
it 'works' do
|
265
|
+
check(1, /1 at a time/)
|
266
|
+
check(3, /3 at a time/)
|
199
267
|
end
|
200
268
|
end
|
201
269
|
|