berater 0.6.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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