berater 0.6.1 → 0.9.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.
@@ -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
@@ -1,7 +1,9 @@
1
1
  describe Berater::Inhibitor do
2
+ subject { described_class.new }
3
+
2
4
  describe '.new' do
3
5
  it 'initializes without any arguments or options' do
4
- expect(described_class.new).to be_a described_class
6
+ is_expected.to be_a described_class
5
7
  end
6
8
 
7
9
  it 'initializes with any arguments and options' do
@@ -9,15 +11,18 @@ describe Berater::Inhibitor do
9
11
  end
10
12
 
11
13
  it 'has default values' do
12
- expect(described_class.new.key).to be :inhibitor
13
- expect(described_class.new.redis).to be Berater.redis
14
+ expect(subject.key).to be :inhibitor
15
+ expect(subject.redis).to be Berater.redis
14
16
  end
15
17
  end
16
18
 
17
19
  describe '#limit' do
18
- subject { described_class.new }
19
-
20
20
  it_behaves_like 'it is overloaded'
21
21
  end
22
22
 
23
+ describe '#to_s' do
24
+ it do
25
+ expect(subject.to_s).to include described_class.to_s
26
+ end
27
+ end
23
28
  end
@@ -0,0 +1,173 @@
1
+ describe Berater::LimiterSet do
2
+ subject { described_class.new }
3
+
4
+ let(:unlimiter) { Berater::Unlimiter.new }
5
+ let(:inhibitor) { Berater::Inhibitor.new }
6
+
7
+ describe '#each' do
8
+ it 'returns an Enumerator' do
9
+ expect(subject.each).to be_a Enumerator
10
+ end
11
+
12
+ it 'works with an empty set' do
13
+ expect(subject.each.to_a).to eq []
14
+ end
15
+
16
+ it 'returns elements' do
17
+ subject << unlimiter
18
+ expect(subject.each.to_a).to eq [ unlimiter ]
19
+ end
20
+ end
21
+
22
+ describe '#<<' do
23
+ it 'adds a limiter' do
24
+ subject << unlimiter
25
+ expect(subject.each.to_a).to eq [ unlimiter ]
26
+ end
27
+
28
+ it 'rejects things that are not limiters' do
29
+ expect {
30
+ subject << :foo
31
+ }.to raise_error(ArgumentError)
32
+ end
33
+
34
+ it 'updates existing keys' do
35
+ limiter = Berater::Unlimiter.new
36
+ expect(limiter).to eq unlimiter
37
+ expect(limiter).not_to be unlimiter
38
+
39
+ subject << unlimiter
40
+ subject << limiter
41
+
42
+ expect(subject.each.to_a).to eq [ limiter ]
43
+ end
44
+ end
45
+
46
+ describe '[]=' do
47
+ it 'adds a limiter' do
48
+ subject[:key] = unlimiter
49
+
50
+ expect(subject.each.to_a).to eq [ unlimiter ]
51
+ is_expected.to include :key
52
+ is_expected.to include unlimiter
53
+ end
54
+
55
+ it 'rejects things that are not limiters' do
56
+ expect {
57
+ subject[:key] = :foo
58
+ }.to raise_error(ArgumentError)
59
+ end
60
+ end
61
+
62
+ describe '#[]' do
63
+ it 'returns nil for missing keys' do
64
+ expect(subject[:key]).to be nil
65
+ expect(subject[nil]).to be nil
66
+ end
67
+
68
+ it 'retreives limiters' do
69
+ subject << unlimiter
70
+ expect(subject[unlimiter.key]).to be unlimiter
71
+ end
72
+ end
73
+
74
+ describe '#fetch' do
75
+ it 'raises for missing keys' do
76
+ expect {
77
+ subject.fetch(:key)
78
+ }.to raise_error(KeyError)
79
+
80
+ expect {
81
+ subject.fetch(nil)
82
+ }.to raise_error(KeyError)
83
+ end
84
+
85
+ it 'returns the default if provided' do
86
+ expect(subject.fetch(:key, unlimiter)).to be unlimiter
87
+ end
88
+
89
+ it 'calls the default proc if provided' do
90
+ expect {|block| subject.fetch(:key, &block) }.to yield_control
91
+ end
92
+
93
+ it 'retreives limiters' do
94
+ subject << unlimiter
95
+ expect(subject.fetch(unlimiter.key)).to be unlimiter
96
+ expect(subject.fetch(unlimiter.key, :default)).to be unlimiter
97
+ end
98
+ end
99
+
100
+ describe '#include?' do
101
+ before do
102
+ subject << unlimiter
103
+ end
104
+
105
+ it 'works with keys' do
106
+ is_expected.to include unlimiter.key
107
+ end
108
+
109
+ it 'works with limiters' do
110
+ is_expected.to include unlimiter
111
+ end
112
+
113
+ it 'works when target is missing' do
114
+ is_expected.not_to include inhibitor.key
115
+ is_expected.not_to include inhibitor
116
+ end
117
+ end
118
+
119
+ describe '#clear' do
120
+ it 'works when empty' do
121
+ subject.clear
122
+ end
123
+
124
+ it 'clears limiters' do
125
+ subject << unlimiter
126
+ is_expected.to include unlimiter
127
+
128
+ subject.clear
129
+ is_expected.not_to include unlimiter
130
+ end
131
+ end
132
+
133
+ describe '#count' do
134
+ it 'counts' do
135
+ expect(subject.count).to be 0
136
+
137
+ subject << unlimiter
138
+ expect(subject.count).to be 1
139
+ end
140
+ end
141
+
142
+ describe '#delete' do
143
+ it 'works when the target is missing' do
144
+ subject.delete(unlimiter)
145
+ subject.delete(unlimiter.key)
146
+ end
147
+
148
+ it 'works with keys' do
149
+ subject << unlimiter
150
+ is_expected.to include unlimiter
151
+
152
+ subject.delete(unlimiter.key)
153
+ is_expected.not_to include unlimiter
154
+ end
155
+
156
+ it 'works with limiters' do
157
+ subject << unlimiter
158
+ is_expected.to include unlimiter
159
+
160
+ subject.delete(unlimiter)
161
+ is_expected.not_to include unlimiter
162
+ end
163
+ end
164
+
165
+ describe '#empty?' do
166
+ it 'works' do
167
+ is_expected.to be_empty
168
+
169
+ subject << unlimiter
170
+ is_expected.not_to be_empty
171
+ end
172
+ end
173
+ end
data/spec/limiter_spec.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  describe Berater::Limiter do
2
- it 'can not be initialized' do
3
- expect { described_class.new }.to raise_error(NotImplementedError)
2
+ describe '.new' do
3
+ it 'can only be called on subclasses' do
4
+ expect { described_class.new }.to raise_error(NoMethodError)
5
+ end
4
6
  end
5
7
 
6
8
  describe 'abstract methods' do
@@ -8,11 +10,78 @@ describe Berater::Limiter do
8
10
 
9
11
  it do
10
12
  expect { limiter.limit }.to raise_error(NotImplementedError)
11
- expect { limiter.overloaded? }.to raise_error(NotImplementedError)
13
+ expect { limiter.utilization }.to raise_error(NotImplementedError)
12
14
  end
13
15
  end
14
16
 
15
- describe '==' do
17
+ describe '#limit' do
18
+ subject { Berater::Unlimiter.new }
19
+
20
+ context 'with a capacity parameter' do
21
+ it 'overrides the stored value' do
22
+ is_expected.to receive(:acquire_lock).with(3, anything)
23
+
24
+ subject.limit(capacity: 3)
25
+ end
26
+
27
+ it 'validates the type' do
28
+ expect {
29
+ subject.limit(capacity: 'abc')
30
+ }.to raise_error(ArgumentError)
31
+ end
32
+ end
33
+
34
+ context 'with a cost parameter' do
35
+ it 'overrides the stored value' do
36
+ is_expected.to receive(:acquire_lock).with(anything, 2)
37
+
38
+ subject.limit(cost: 2)
39
+ end
40
+
41
+ it 'validates' do
42
+ expect {
43
+ subject.limit(cost: 'abc')
44
+ }.to raise_error(ArgumentError)
45
+
46
+ expect {
47
+ subject.limit(cost: -1)
48
+ }.to raise_error(ArgumentError)
49
+
50
+ expect {
51
+ subject.limit(cost: Float::INFINITY)
52
+ }.to raise_error(ArgumentError)
53
+ end
54
+ end
55
+
56
+ context 'when Berater.redis is nil' do
57
+ let!(:redis) { Berater.redis }
58
+
59
+ before { Berater.redis = nil }
60
+
61
+ it 'works with Unlimiter since redis is not used' do
62
+ expect(subject.redis).to be nil
63
+ expect {|b| subject.limit(&b) }.to yield_control
64
+ end
65
+
66
+ it 'raises when redis is needed' do
67
+ limiter = Berater::RateLimiter.new(:key, 1, :second)
68
+ expect(limiter.redis).to be nil
69
+ expect { limiter.limit }.to raise_error(RuntimeError)
70
+ end
71
+
72
+ it 'works when redis is passed in' do
73
+ limiter = Berater::RateLimiter.new(:key, 1, :second, redis: redis)
74
+ expect {|b| limiter.limit(&b) }.to yield_control
75
+ end
76
+
77
+ it 'raises when redis is bogus' do
78
+ limiter = Berater::RateLimiter.new(:key, 1, :second, redis: :stub)
79
+ expect { limiter.limit }.to raise_error(RuntimeError)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#==' do
16
85
  let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
17
86
 
18
87
  it 'equals itself' do
@@ -25,12 +94,6 @@ describe Berater::Limiter do
25
94
  )
26
95
  end
27
96
 
28
- it 'equals something with equvalent initialization parameters' do
29
- expect(limiter).to eq(
30
- Berater::RateLimiter.new(:key, 1, 1)
31
- )
32
- end
33
-
34
97
  it 'does not equal something different' do
35
98
  expect(limiter).not_to eq(
36
99
  Berater::RateLimiter.new(:key, 2, :second)
@@ -68,4 +131,56 @@ describe Berater::Limiter do
68
131
  end
69
132
  end
70
133
 
134
+ describe '#cache_key' do
135
+ subject { klass.new(:key).send(:cache_key) }
136
+
137
+ context 'with Unlimiter' do
138
+ let(:klass) { Berater::Unlimiter }
139
+
140
+ it do
141
+ is_expected.to eq 'Berater:Unlimiter:key'
142
+ end
143
+ end
144
+
145
+ context 'with custom limiter' do
146
+ MyLimiter = Class.new(Berater::Unlimiter)
147
+
148
+ let(:klass) { MyLimiter }
149
+
150
+ it 'adds Berater prefix' do
151
+ is_expected.to eq 'Berater:MyLimiter:key'
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '.cache_key' do
157
+ subject { klass.send(:cache_key, :key) }
158
+
159
+ context 'with Unlimiter' do
160
+ let(:klass) { Berater::Unlimiter }
161
+
162
+ it do
163
+ is_expected.to eq 'Berater:Unlimiter:key'
164
+ end
165
+ end
166
+
167
+ context 'with custom limiter' do
168
+ MyLimiter = Class.new(Berater::Unlimiter)
169
+
170
+ let(:klass) { MyLimiter }
171
+
172
+ it 'adds Berater prefix' do
173
+ is_expected.to eq 'Berater:MyLimiter:key'
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '.inherited' do
179
+ it 'creates convenience methods' do
180
+ expect(Berater.method(:Unlimiter)).to be_a Method
181
+ expect(Berater::Unlimiter()).to be_a Berater::Unlimiter
182
+ expect {|b| Berater::Unlimiter(&b) }.to yield_control
183
+ end
184
+ end
185
+
71
186
  end
@@ -1,118 +1,80 @@
1
1
  describe Berater::Matchers::Overloaded do
2
2
 
3
3
  context 'Berater::Unlimiter' do
4
- let(:limiter) { Berater.new(:key, :unlimited) }
4
+ let(:limiter) { Berater::Unlimiter.new }
5
5
 
6
6
  it { expect(limiter).not_to be_overloaded }
7
- it { expect(limiter).not_to be_inhibited }
8
- it { expect(limiter).not_to be_overrated }
9
- it { expect(limiter).not_to be_incapacitated }
10
-
11
7
  it { expect { limiter }.not_to be_overloaded }
12
- it { expect { limiter }.not_to be_inhibited }
13
- it { expect { limiter }.not_to be_overrated }
14
- it { expect { limiter }.not_to be_incapacitated }
15
-
16
8
  it { expect { limiter.limit }.not_to be_overloaded }
17
- it { expect { limiter.limit }.not_to be_inhibited }
18
- it { expect { limiter.limit }.not_to be_overrated }
19
- it { expect { limiter.limit }.not_to be_incapacitated }
20
9
  end
21
10
 
22
11
  context 'Berater::Inhibitor' do
23
- let(:limiter) { Berater.new(:key, :inhibited) }
12
+ let(:limiter) { Berater::Inhibitor.new }
24
13
 
25
14
  it { expect(limiter).to be_overloaded }
26
- it { expect(limiter).to be_inhibited }
27
-
28
15
  it { expect { limiter }.to be_overloaded }
29
- it { expect { limiter }.to be_inhibited }
30
-
31
16
  it { expect { limiter.limit }.to be_overloaded }
32
- it { expect { limiter.limit }.to be_inhibited }
33
17
  end
34
18
 
35
19
  context 'Berater::RateLimiter' do
36
- let(:limiter) { Berater.new(:key, 1, :second) }
20
+ let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
37
21
 
38
22
  it { expect(limiter).not_to be_overloaded }
39
- it { expect(limiter).not_to be_inhibited }
40
- it { expect(limiter).not_to be_overrated }
41
- it { expect(limiter).not_to be_incapacitated }
42
-
43
23
  it { expect { limiter }.not_to be_overloaded }
44
- it { expect { limiter }.not_to be_inhibited }
45
- it { expect { limiter }.not_to be_overrated }
46
- it { expect { limiter }.not_to be_incapacitated }
47
-
48
24
  it { expect { limiter.limit }.not_to be_overloaded }
49
- it { expect { limiter.limit }.not_to be_inhibited }
50
- it { expect { limiter.limit }.not_to be_overrated }
51
- it { expect { limiter.limit }.not_to be_incapacitated }
52
25
 
53
26
  context 'once limit is used up' do
54
27
  before { limiter.limit }
55
28
 
56
- it 'should be_overrated' do
57
- expect(limiter).to be_overrated
29
+ it 'should be_overloaded' do
30
+ expect(limiter).to be_overloaded
58
31
  end
59
32
 
60
- it 'should be_overrated' do
61
- expect { limiter }.to be_overrated
33
+ it 'should be_overloaded' do
34
+ expect { limiter }.to be_overloaded
62
35
  end
63
36
 
64
- it 'should be_overrated' do
65
- expect { limiter.limit }.to be_overrated
37
+ it 'should be_overloaded' do
38
+ expect { limiter.limit }.to be_overloaded
66
39
  end
67
40
  end
68
41
  end
69
42
 
70
43
  context 'Berater::ConcurrencyLimiter' do
71
- let(:limiter) { Berater.new(:key, 1) }
44
+ let(:limiter) { Berater::ConcurrencyLimiter.new(:key, 1) }
72
45
 
73
46
  it { expect(limiter).not_to be_overloaded }
74
- it { expect(limiter).not_to be_inhibited }
75
- it { expect(limiter).not_to be_overrated }
76
- it { expect(limiter).not_to be_incapacitated }
77
-
78
47
  it { expect { limiter }.not_to be_overloaded }
79
- it { expect { limiter }.not_to be_inhibited }
80
- it { expect { limiter }.not_to be_overrated }
81
- it { expect { limiter }.not_to be_incapacitated }
82
-
83
48
  it { expect { limiter.limit }.not_to be_overloaded }
84
- it { expect { limiter.limit }.not_to be_inhibited }
85
- it { expect { limiter.limit }.not_to be_overrated }
86
- it { expect { limiter.limit }.not_to be_incapacitated }
87
49
 
88
50
  context 'when lock is released' do
89
- it 'should be_incapacitated' do
51
+ it 'should be_overloaded' do
90
52
  3.times do
91
- expect(limiter).not_to be_incapacitated
53
+ expect(limiter).not_to be_overloaded
92
54
  end
93
55
  end
94
56
 
95
- it 'should be_incapacitated' do
57
+ it 'should be_overloaded' do
96
58
  3.times do
97
- expect { limiter }.not_to be_incapacitated
59
+ expect { limiter }.not_to be_overloaded
98
60
  end
99
61
  end
100
62
 
101
- it 'should be_incapacitated' do
63
+ it 'should be_overloaded' do
102
64
  3.times do
103
- expect { limiter.limit {} }.not_to be_incapacitated
65
+ expect { limiter.limit {} }.not_to be_overloaded
104
66
  end
105
67
  end
106
68
  end
107
69
 
108
70
  context 'when lock is *not* released' do
109
- it 'should be_incapacitated' do
110
- expect { limiter.limit }.not_to be_incapacitated
111
- expect { limiter.limit }.to be_incapacitated
71
+ it 'should be_overloaded' do
72
+ expect { limiter.limit }.not_to be_overloaded
73
+ expect { limiter.limit }.to be_overloaded
112
74
  end
113
75
 
114
- it 'should be_incapacitated' do
115
- expect { 3.times { limiter.limit } }.to be_incapacitated
76
+ it 'should be_overloaded' do
77
+ expect { 3.times { limiter.limit } }.to be_overloaded
116
78
  end
117
79
  end
118
80
  end
@@ -156,31 +118,5 @@ describe Berater::Matchers::Overloaded do
156
118
  expect { raise Berater::Overloaded }.not_to be_overloaded
157
119
  }.to fail_including("did not expect #{Berater::Overloaded} to be raised")
158
120
  end
159
-
160
- it 'supports different verbs' do
161
- expect {
162
- expect { unlimiter }.to be_overrated
163
- }.to fail_including('expected to be overrated')
164
-
165
- expect {
166
- expect { unlimiter }.to be_incapacitated
167
- }.to fail_including('expected to be incapacitated')
168
- end
169
-
170
- it 'supports different exceptions' do
171
- expect {
172
- expect { 123 }.to be_overrated
173
- }.to fail_including(
174
- "expected #{Berater::RateLimiter::Overrated} to be raised"
175
- )
176
-
177
- expect {
178
- expect {
179
- raise Berater::ConcurrencyLimiter::Incapacitated
180
- }.not_to be_incapacitated
181
- }.to fail_including(
182
- "did not expect #{Berater::ConcurrencyLimiter::Incapacitated} to be raised"
183
- )
184
- end
185
121
  end
186
122
  end