berater 0.5.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/berater.rb +10 -7
- data/lib/berater/concurrency_limiter.rb +18 -27
- data/lib/berater/dsl.rb +8 -8
- data/lib/berater/inhibitor.rb +2 -9
- data/lib/berater/limiter.rb +44 -20
- data/lib/berater/lock.rb +3 -4
- data/lib/berater/rate_limiter.rb +43 -40
- data/lib/berater/rspec.rb +2 -2
- data/lib/berater/rspec/matchers.rb +37 -38
- data/lib/berater/test_mode.rb +13 -29
- data/lib/berater/unlimiter.rb +6 -4
- data/lib/berater/utils.rb +4 -4
- data/lib/berater/version.rb +1 -1
- data/spec/berater_spec.rb +9 -9
- data/spec/concurrency_limiter_spec.rb +82 -55
- data/spec/dsl_refinement_spec.rb +0 -12
- data/spec/dsl_spec.rb +5 -17
- data/spec/limiter_spec.rb +39 -3
- data/spec/matchers_spec.rb +64 -72
- data/spec/rate_limiter_spec.rb +134 -44
- data/spec/riddle_spec.rb +6 -2
- data/spec/test_mode_spec.rb +40 -111
- data/spec/utils_spec.rb +30 -30
- metadata +2 -2
data/spec/dsl_refinement_spec.rb
CHANGED
@@ -3,18 +3,6 @@ require 'berater/dsl'
|
|
3
3
|
describe Berater do
|
4
4
|
using Berater::DSL
|
5
5
|
|
6
|
-
it 'instatiates an Unlimiter' do
|
7
|
-
limiter = Berater.new(:key) { unlimited }
|
8
|
-
expect(limiter).to be_a Berater::Unlimiter
|
9
|
-
expect(limiter.key).to be :key
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'instatiates an Inhibiter' do
|
13
|
-
limiter = Berater.new(:key) { inhibited }
|
14
|
-
expect(limiter).to be_a Berater::Inhibitor
|
15
|
-
expect(limiter.key).to be :key
|
16
|
-
end
|
17
|
-
|
18
6
|
it 'instatiates a RateLimiter' do
|
19
7
|
limiter = Berater.new(:key) { 1.per second }
|
20
8
|
expect(limiter).to be_a Berater::RateLimiter
|
data/spec/dsl_spec.rb
CHANGED
@@ -13,13 +13,13 @@ describe Berater::DSL do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'parses' do
|
16
|
-
check([ 1, :second ]) { 1.per second }
|
17
|
-
check([ 3, :minute ]) { 3.per minute }
|
18
|
-
check([ 5, :hour ]) { 5.every hour }
|
16
|
+
check([ 1, interval: :second ]) { 1.per second }
|
17
|
+
check([ 3, interval: :minute ]) { 3.per minute }
|
18
|
+
check([ 5, interval: :hour ]) { 5.every hour }
|
19
19
|
end
|
20
20
|
|
21
21
|
it 'cleans up afterward' do
|
22
|
-
check([ 1, :second ]) { 1.per second }
|
22
|
+
check([ 1, interval: :second ]) { 1.per second }
|
23
23
|
|
24
24
|
expect(Integer).not_to respond_to(:per)
|
25
25
|
expect(Integer).not_to respond_to(:every)
|
@@ -29,7 +29,7 @@ describe Berater::DSL do
|
|
29
29
|
count = 1
|
30
30
|
interval = :second
|
31
31
|
|
32
|
-
check([ count, interval ]) { count.per interval }
|
32
|
+
check([ count, interval: interval ]) { count.per interval }
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -57,16 +57,4 @@ describe Berater::DSL do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
context 'unlimited mode' do
|
61
|
-
it 'has keywords' do
|
62
|
-
check(:unlimited) { unlimited }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
context 'inhibited mode' do
|
67
|
-
it 'has keywords' do
|
68
|
-
check(:inhibited) { inhibited }
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
60
|
end
|
data/spec/limiter_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
describe Berater::Limiter do
|
2
2
|
it 'can not be initialized' do
|
3
|
-
expect { described_class.new }.to raise_error(
|
3
|
+
expect { described_class.new }.to raise_error(NoMethodError)
|
4
4
|
end
|
5
5
|
|
6
6
|
describe 'abstract methods' do
|
@@ -8,11 +8,47 @@ describe Berater::Limiter do
|
|
8
8
|
|
9
9
|
it do
|
10
10
|
expect { limiter.limit }.to raise_error(NotImplementedError)
|
11
|
-
expect { limiter.
|
11
|
+
expect { limiter.utilization }.to raise_error(NotImplementedError)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
describe '
|
15
|
+
describe '#limit' do
|
16
|
+
subject { Berater::Unlimiter.new }
|
17
|
+
|
18
|
+
context 'with a capacity parameter' do
|
19
|
+
it 'overrides the stored value' do
|
20
|
+
is_expected.to receive(:acquire_lock).with(3, anything)
|
21
|
+
|
22
|
+
subject.limit(capacity: 3)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'validates the type' do
|
26
|
+
expect {
|
27
|
+
subject.limit(capacity: 'abc')
|
28
|
+
}.to raise_error(ArgumentError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with a cost parameter' do
|
33
|
+
it 'overrides the stored value' do
|
34
|
+
is_expected.to receive(:acquire_lock).with(anything, 2)
|
35
|
+
|
36
|
+
subject.limit(cost: 2)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'validates' do
|
40
|
+
expect {
|
41
|
+
subject.limit(cost: 'abc')
|
42
|
+
}.to raise_error(ArgumentError)
|
43
|
+
|
44
|
+
expect {
|
45
|
+
subject.limit(cost: -1)
|
46
|
+
}.to raise_error(ArgumentError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#==' do
|
16
52
|
let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
|
17
53
|
|
18
54
|
it 'equals itself' do
|
data/spec/matchers_spec.rb
CHANGED
@@ -1,130 +1,122 @@
|
|
1
|
-
describe
|
1
|
+
describe Berater::Matchers::Overloaded do
|
2
|
+
|
2
3
|
context 'Berater::Unlimiter' do
|
3
|
-
let(:limiter) { Berater.new
|
4
|
+
let(:limiter) { Berater::Unlimiter.new }
|
4
5
|
|
5
6
|
it { expect(limiter).not_to be_overloaded }
|
6
|
-
it { expect(limiter).not_to be_inhibited }
|
7
|
-
it { expect(limiter).not_to be_overrated }
|
8
|
-
it { expect(limiter).not_to be_incapacitated }
|
9
|
-
|
10
7
|
it { expect { limiter }.not_to be_overloaded }
|
11
|
-
it { expect { limiter }.not_to be_inhibited }
|
12
|
-
it { expect { limiter }.not_to be_overrated }
|
13
|
-
it { expect { limiter }.not_to be_incapacitated }
|
14
|
-
|
15
8
|
it { expect { limiter.limit }.not_to be_overloaded }
|
16
|
-
it { expect { limiter.limit }.not_to be_inhibited }
|
17
|
-
it { expect { limiter.limit }.not_to be_overrated }
|
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
|
25
9
|
end
|
26
10
|
|
27
11
|
context 'Berater::Inhibitor' do
|
28
|
-
let(:limiter) { Berater.new
|
12
|
+
let(:limiter) { Berater::Inhibitor.new }
|
29
13
|
|
30
14
|
it { expect(limiter).to be_overloaded }
|
31
|
-
it { expect(limiter).to be_inhibited }
|
32
|
-
|
33
15
|
it { expect { limiter }.to be_overloaded }
|
34
|
-
it { expect { limiter }.to be_inhibited }
|
35
|
-
|
36
16
|
it { expect { limiter.limit }.to be_overloaded }
|
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
|
44
17
|
end
|
45
18
|
|
46
19
|
context 'Berater::RateLimiter' do
|
47
|
-
let(:limiter) { Berater.new(:key, 1, :second) }
|
20
|
+
let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
|
48
21
|
|
49
22
|
it { expect(limiter).not_to be_overloaded }
|
50
|
-
it { expect(limiter).not_to be_inhibited }
|
51
|
-
it { expect(limiter).not_to be_overrated }
|
52
|
-
it { expect(limiter).not_to be_incapacitated }
|
53
|
-
|
54
23
|
it { expect { limiter }.not_to be_overloaded }
|
55
|
-
it { expect { limiter }.not_to be_inhibited }
|
56
|
-
it { expect { limiter }.not_to be_overrated }
|
57
|
-
it { expect { limiter }.not_to be_incapacitated }
|
58
|
-
|
59
24
|
it { expect { limiter.limit }.not_to be_overloaded }
|
60
|
-
it { expect { limiter.limit }.not_to be_inhibited }
|
61
|
-
it { expect { limiter.limit }.not_to be_overrated }
|
62
|
-
it { expect { limiter.limit }.not_to be_incapacitated }
|
63
25
|
|
64
26
|
context 'once limit is used up' do
|
65
27
|
before { limiter.limit }
|
66
28
|
|
67
|
-
it 'should
|
68
|
-
expect(limiter).to
|
29
|
+
it 'should be_overloaded' do
|
30
|
+
expect(limiter).to be_overloaded
|
69
31
|
end
|
70
32
|
|
71
|
-
it 'should
|
72
|
-
expect { limiter }.to
|
33
|
+
it 'should be_overloaded' do
|
34
|
+
expect { limiter }.to be_overloaded
|
73
35
|
end
|
74
36
|
|
75
|
-
it 'should
|
76
|
-
expect { limiter.limit }.to
|
37
|
+
it 'should be_overloaded' do
|
38
|
+
expect { limiter.limit }.to be_overloaded
|
77
39
|
end
|
78
40
|
end
|
79
41
|
end
|
80
42
|
|
81
43
|
context 'Berater::ConcurrencyLimiter' do
|
82
|
-
let(:limiter) { Berater.new(:key, 1) }
|
44
|
+
let(:limiter) { Berater::ConcurrencyLimiter.new(:key, 1) }
|
83
45
|
|
84
46
|
it { expect(limiter).not_to be_overloaded }
|
85
|
-
it { expect(limiter).not_to be_inhibited }
|
86
|
-
it { expect(limiter).not_to be_overrated }
|
87
|
-
it { expect(limiter).not_to be_incapacitated }
|
88
|
-
|
89
47
|
it { expect { limiter }.not_to be_overloaded }
|
90
|
-
it { expect { limiter }.not_to be_inhibited }
|
91
|
-
it { expect { limiter }.not_to be_overrated }
|
92
|
-
it { expect { limiter }.not_to be_incapacitated }
|
93
|
-
|
94
48
|
it { expect { limiter.limit }.not_to be_overloaded }
|
95
|
-
it { expect { limiter.limit }.not_to be_inhibited }
|
96
|
-
it { expect { limiter.limit }.not_to be_overrated }
|
97
|
-
it { expect { limiter.limit }.not_to be_incapacitated }
|
98
49
|
|
99
50
|
context 'when lock is released' do
|
100
|
-
it 'should
|
51
|
+
it 'should be_overloaded' do
|
101
52
|
3.times do
|
102
|
-
expect(limiter).not_to
|
53
|
+
expect(limiter).not_to be_overloaded
|
103
54
|
end
|
104
55
|
end
|
105
56
|
|
106
|
-
it 'should
|
57
|
+
it 'should be_overloaded' do
|
107
58
|
3.times do
|
108
|
-
expect { limiter }.not_to
|
59
|
+
expect { limiter }.not_to be_overloaded
|
109
60
|
end
|
110
61
|
end
|
111
62
|
|
112
|
-
it 'should
|
63
|
+
it 'should be_overloaded' do
|
113
64
|
3.times do
|
114
|
-
expect { limiter.limit {} }.not_to
|
65
|
+
expect { limiter.limit {} }.not_to be_overloaded
|
115
66
|
end
|
116
67
|
end
|
117
68
|
end
|
118
69
|
|
119
70
|
context 'when lock is *not* released' do
|
120
|
-
it 'should
|
121
|
-
expect { limiter.limit }.not_to
|
122
|
-
expect { limiter.limit }.to
|
71
|
+
it 'should be_overloaded' do
|
72
|
+
expect { limiter.limit }.not_to be_overloaded
|
73
|
+
expect { limiter.limit }.to be_overloaded
|
123
74
|
end
|
124
75
|
|
125
|
-
it 'should
|
126
|
-
expect { 3.times { limiter.limit } }.to
|
76
|
+
it 'should be_overloaded' do
|
77
|
+
expect { 3.times { limiter.limit } }.to be_overloaded
|
127
78
|
end
|
128
79
|
end
|
129
80
|
end
|
81
|
+
|
82
|
+
context 'when matchers fail' do
|
83
|
+
let(:unlimiter) { Berater::Unlimiter.new }
|
84
|
+
let(:inhibitor) { Berater::Inhibitor.new }
|
85
|
+
|
86
|
+
it 'catches false negatives' do
|
87
|
+
expect {
|
88
|
+
expect(unlimiter).to be_overloaded
|
89
|
+
}.to fail_including('expected to be overloaded')
|
90
|
+
|
91
|
+
expect {
|
92
|
+
expect { unlimiter }.to be_overloaded
|
93
|
+
}.to fail_including('expected to be overloaded')
|
94
|
+
|
95
|
+
expect {
|
96
|
+
expect { unlimiter.limit }.to be_overloaded
|
97
|
+
}.to fail_including("expected #{Berater::Overloaded} to be raised")
|
98
|
+
|
99
|
+
expect {
|
100
|
+
expect { 123 }.to be_overloaded
|
101
|
+
}.to fail_including("expected #{Berater::Overloaded} to be raised")
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'catches false positives' do
|
105
|
+
expect {
|
106
|
+
expect(inhibitor).not_to be_overloaded
|
107
|
+
}.to fail_including('expected not to be overloaded')
|
108
|
+
|
109
|
+
expect {
|
110
|
+
expect { inhibitor }.not_to be_overloaded
|
111
|
+
}.to fail_including('expected not to be overloaded')
|
112
|
+
|
113
|
+
expect {
|
114
|
+
expect { inhibitor.limit }.not_to be_overloaded
|
115
|
+
}.to fail_including("did not expect #{Berater::Overloaded} to be raised")
|
116
|
+
|
117
|
+
expect {
|
118
|
+
expect { raise Berater::Overloaded }.not_to be_overloaded
|
119
|
+
}.to fail_including("did not expect #{Berater::Overloaded} to be raised")
|
120
|
+
end
|
121
|
+
end
|
130
122
|
end
|
data/spec/rate_limiter_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
describe Berater::RateLimiter do
|
2
|
-
it_behaves_like 'a limiter',
|
2
|
+
it_behaves_like 'a limiter', described_class.new(:key, 3, :second)
|
3
|
+
it_behaves_like 'a limiter', described_class.new(:key, 3.5, :second)
|
3
4
|
|
4
5
|
describe '.new' do
|
5
6
|
let(:limiter) { described_class.new(:key, 1, :second) }
|
@@ -23,6 +24,7 @@ describe Berater::RateLimiter do
|
|
23
24
|
|
24
25
|
it { expect_capacity(0) }
|
25
26
|
it { expect_capacity(1) }
|
27
|
+
it { expect_capacity(1.5) }
|
26
28
|
it { expect_capacity(100) }
|
27
29
|
|
28
30
|
context 'with erroneous values' do
|
@@ -32,7 +34,6 @@ describe Berater::RateLimiter do
|
|
32
34
|
end.to raise_error ArgumentError
|
33
35
|
end
|
34
36
|
|
35
|
-
it { expect_bad_capacity(0.5) }
|
36
37
|
it { expect_bad_capacity(-1) }
|
37
38
|
it { expect_bad_capacity('1') }
|
38
39
|
it { expect_bad_capacity(:one) }
|
@@ -44,9 +45,19 @@ describe Berater::RateLimiter do
|
|
44
45
|
|
45
46
|
subject { described_class.new(:key, 1, :second) }
|
46
47
|
|
47
|
-
it 'saves the interval in original and
|
48
|
+
it 'saves the interval in original and millisecond format' do
|
48
49
|
expect(subject.interval).to be :second
|
49
|
-
expect(subject.instance_variable_get(:@
|
50
|
+
expect(subject.instance_variable_get(:@interval_msec)).to be 10**3
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'must be > 0' do
|
54
|
+
expect {
|
55
|
+
described_class.new(:key, 1, 0)
|
56
|
+
}.to raise_error(ArgumentError)
|
57
|
+
|
58
|
+
expect {
|
59
|
+
described_class.new(:key, 1, -1)
|
60
|
+
}.to raise_error(ArgumentError)
|
50
61
|
end
|
51
62
|
end
|
52
63
|
|
@@ -65,56 +76,129 @@ describe Berater::RateLimiter do
|
|
65
76
|
it 'limits excessive calls' do
|
66
77
|
3.times { limiter.limit }
|
67
78
|
|
68
|
-
expect(limiter).to
|
79
|
+
expect(limiter).to be_overloaded
|
69
80
|
end
|
70
81
|
|
71
|
-
it 'limit
|
82
|
+
it 'resets limit over time' do
|
72
83
|
3.times { limiter.limit }
|
73
|
-
expect(limiter).to
|
74
|
-
|
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
|
84
|
+
expect(limiter).to be_overloaded
|
83
85
|
|
84
|
-
# traveling 1 second will reset the count
|
85
86
|
Timecop.freeze(1)
|
86
87
|
|
87
88
|
3.times { limiter.limit }
|
88
|
-
expect(limiter).to
|
89
|
+
expect(limiter).to be_overloaded
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with millisecond precision' do
|
93
|
+
it 'resets limit over time' do
|
94
|
+
3.times { limiter.limit }
|
95
|
+
expect(limiter).to be_overloaded
|
96
|
+
|
97
|
+
# travel forward to just before the count decrements
|
98
|
+
Timecop.freeze(0.333)
|
99
|
+
expect(limiter).to be_overloaded
|
100
|
+
|
101
|
+
# traveling one more millisecond will decrement the count
|
102
|
+
Timecop.freeze(0.001)
|
103
|
+
limiter.limit
|
104
|
+
expect(limiter).to be_overloaded
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'works when drip rate is < 1 per millisecond' do
|
108
|
+
limiter = described_class.new(:key, 2_000, :second)
|
109
|
+
|
110
|
+
limiter.capacity.times { limiter.limit }
|
111
|
+
expect(limiter).to be_overloaded
|
112
|
+
|
113
|
+
Timecop.freeze(0.001)
|
114
|
+
expect(limiter).not_to be_overloaded
|
115
|
+
|
116
|
+
2.times { limiter.limit }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when capacity is a Float' do
|
121
|
+
let(:limiter) { described_class.new(:key, 1.5, :second) }
|
122
|
+
|
123
|
+
it 'still works' do
|
124
|
+
limiter.limit
|
125
|
+
expect(limiter).not_to be_overloaded
|
126
|
+
|
127
|
+
expect { limiter.limit }.to be_overloaded
|
128
|
+
|
129
|
+
limiter.limit(cost: 0.5)
|
130
|
+
end
|
89
131
|
end
|
90
132
|
|
91
133
|
it 'accepts a dynamic capacity' do
|
92
134
|
limiter = described_class.new(:key, 1, :second)
|
93
135
|
|
94
|
-
expect { limiter.limit(capacity: 0) }.to
|
136
|
+
expect { limiter.limit(capacity: 0) }.to be_overloaded
|
95
137
|
5.times { limiter.limit(capacity: 10) }
|
96
|
-
expect { limiter }.to
|
138
|
+
expect { limiter }.to be_overloaded
|
97
139
|
end
|
98
140
|
|
99
141
|
context 'works with cost parameter' do
|
100
|
-
it { expect { limiter.limit(cost: 4) }.to
|
142
|
+
it { expect { limiter.limit(cost: 4) }.to be_overloaded }
|
101
143
|
|
102
144
|
it 'works within limit' do
|
103
145
|
limiter.limit(cost: 3)
|
104
|
-
expect { limiter.limit }.to
|
146
|
+
expect { limiter.limit }.to be_overloaded
|
105
147
|
end
|
106
148
|
|
107
149
|
it 'resets over time' do
|
108
150
|
limiter.limit(cost: 3)
|
109
|
-
expect(limiter).to
|
151
|
+
expect(limiter).to be_overloaded
|
110
152
|
|
111
153
|
Timecop.freeze(1)
|
112
|
-
expect(limiter).not_to
|
154
|
+
expect(limiter).not_to be_overloaded
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when cost is a Float' do
|
158
|
+
it 'still works' do
|
159
|
+
2.times { limiter.limit(cost: 1.5) }
|
160
|
+
expect(limiter).to be_overloaded
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'calculates contention correctly' do
|
164
|
+
# note: Redis must return Floats as strings to maintain precision
|
165
|
+
lock = limiter.limit(cost: 1.5)
|
166
|
+
expect(lock.contention).to be 1.5
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'with clock skew' do
|
172
|
+
let(:limiter) { described_class.new(:key, 10, :second) }
|
173
|
+
|
174
|
+
it 'works skewing backward' do
|
175
|
+
limiter.limit(cost: 9)
|
176
|
+
|
177
|
+
Timecop.freeze(-0.1) do
|
178
|
+
limiter.limit
|
179
|
+
expect(limiter).to be_overloaded
|
180
|
+
end
|
181
|
+
|
182
|
+
expect(limiter).to be_overloaded
|
183
|
+
|
184
|
+
Timecop.freeze(0.1)
|
185
|
+
limiter.limit
|
186
|
+
expect(limiter).to be_overloaded
|
113
187
|
end
|
114
188
|
|
115
|
-
it '
|
116
|
-
|
117
|
-
|
189
|
+
it 'works skewing forward' do
|
190
|
+
limiter.limit
|
191
|
+
|
192
|
+
Timecop.freeze(0.1) do
|
193
|
+
# one drip later
|
194
|
+
limiter.limit(cost: 10)
|
195
|
+
expect(limiter).to be_overloaded
|
196
|
+
end
|
197
|
+
|
198
|
+
expect(limiter).to be_overloaded
|
199
|
+
|
200
|
+
Timecop.freeze(0.1)
|
201
|
+
expect(limiter).to be_overloaded
|
118
202
|
end
|
119
203
|
end
|
120
204
|
|
@@ -123,10 +207,10 @@ describe Berater::RateLimiter do
|
|
123
207
|
let(:limiter_two) { described_class.new(:key, 1, :second) }
|
124
208
|
|
125
209
|
it 'works as expected' do
|
126
|
-
expect(limiter_one.limit).not_to
|
210
|
+
expect(limiter_one.limit).not_to be_overloaded
|
127
211
|
|
128
|
-
expect(limiter_one).to
|
129
|
-
expect(limiter_two).to
|
212
|
+
expect(limiter_one).to be_overloaded
|
213
|
+
expect(limiter_two).to be_overloaded
|
130
214
|
end
|
131
215
|
end
|
132
216
|
|
@@ -135,27 +219,33 @@ describe Berater::RateLimiter do
|
|
135
219
|
let(:limiter_two) { described_class.new(:two, 2, :second) }
|
136
220
|
|
137
221
|
it 'works as expected' do
|
138
|
-
expect(limiter_one.limit).not_to
|
139
|
-
expect(limiter_two.limit).not_to
|
222
|
+
expect(limiter_one.limit).not_to be_overloaded
|
223
|
+
expect(limiter_two.limit).not_to be_overloaded
|
140
224
|
|
141
|
-
expect(limiter_one).to
|
142
|
-
expect(limiter_two.limit).not_to
|
225
|
+
expect(limiter_one).to be_overloaded
|
226
|
+
expect(limiter_two.limit).not_to be_overloaded
|
143
227
|
|
144
|
-
expect(limiter_one).to
|
145
|
-
expect(limiter_two).to
|
228
|
+
expect(limiter_one).to be_overloaded
|
229
|
+
expect(limiter_two).to be_overloaded
|
146
230
|
end
|
147
231
|
end
|
148
232
|
end
|
149
233
|
|
150
|
-
describe '#
|
151
|
-
let(:limiter) { described_class.new(:key,
|
234
|
+
describe '#utilization' do
|
235
|
+
let(:limiter) { described_class.new(:key, 10, :minute) }
|
152
236
|
|
153
|
-
it
|
154
|
-
expect(limiter.
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
237
|
+
it do
|
238
|
+
expect(limiter.utilization).to be 0.0
|
239
|
+
|
240
|
+
2.times { limiter.limit }
|
241
|
+
expect(limiter.utilization).to be 0.2
|
242
|
+
|
243
|
+
8.times { limiter.limit }
|
244
|
+
expect(limiter.utilization).to be 1.0
|
245
|
+
|
246
|
+
Timecop.freeze(30)
|
247
|
+
|
248
|
+
expect(limiter.utilization).to be 0.5
|
159
249
|
end
|
160
250
|
end
|
161
251
|
|