berater 0.1.1 → 0.3.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.
@@ -1,36 +1,24 @@
1
1
  describe Berater::Inhibitor do
2
- before { Berater.mode = :inhibited }
3
-
4
2
  describe '.new' do
5
3
  it 'initializes without any arguments or options' do
6
4
  expect(described_class.new).to be_a described_class
7
5
  end
8
6
 
9
7
  it 'initializes with any arguments and options' do
10
- expect(described_class.new(:abc, x: 123)).to be_a described_class
8
+ expect(described_class.new(:abc, :def, x: 123)).to be_a described_class
11
9
  end
12
10
 
13
11
  it 'has default values' do
14
- expect(described_class.new.key).to eq described_class.to_s
12
+ expect(described_class.new.key).to be :inhibitor
15
13
  expect(described_class.new.redis).to be Berater.redis
16
14
  end
17
15
  end
18
16
 
19
- describe '.limit' do
20
- it 'always limits' do
21
- expect { described_class.limit }.to be_inhibited
22
- end
23
-
24
- it 'works with any arguments or options' do
25
- expect { described_class.limit(:abc, x: 123) }.to be_inhibited
26
- end
27
- end
28
-
29
17
  describe '#limit' do
30
18
  let(:limiter) { described_class.new }
31
19
 
32
20
  it 'always limits' do
33
- expect { described_class.limit }.to be_inhibited
21
+ expect { limiter.limit }.to be_inhibited
34
22
  end
35
23
  end
36
24
 
@@ -1,6 +1,6 @@
1
1
  describe 'be_overloaded' do
2
2
  context 'Berater::Unlimiter' do
3
- let(:limiter) { Berater.new(:unlimited) }
3
+ let(:limiter) { Berater.new(:key, :unlimited) }
4
4
 
5
5
  it { expect(limiter).not_to be_overloaded }
6
6
  it { expect(limiter).not_to be_inhibited }
@@ -19,7 +19,7 @@ describe 'be_overloaded' do
19
19
  end
20
20
 
21
21
  context 'Berater::Inhibitor' do
22
- let(:limiter) { Berater.new(:inhibited) }
22
+ let(:limiter) { Berater.new(:key, :inhibited) }
23
23
 
24
24
  it { expect(limiter).to be_overloaded }
25
25
  it { expect(limiter).to be_inhibited }
@@ -32,7 +32,7 @@ describe 'be_overloaded' do
32
32
  end
33
33
 
34
34
  context 'Berater::RateLimiter' do
35
- let(:limiter) { Berater.new(:rate, 1, :second) }
35
+ let(:limiter) { Berater.new(:key, :rate, 1, :second) }
36
36
 
37
37
  it { expect(limiter).not_to be_overloaded }
38
38
  it { expect(limiter).not_to be_inhibited }
@@ -67,7 +67,7 @@ describe 'be_overloaded' do
67
67
  end
68
68
 
69
69
  context 'Berater::ConcurrencyLimiter' do
70
- let(:limiter) { Berater.new(:concurrency, 1) }
70
+ let(:limiter) { Berater.new(:key, :concurrency, 1) }
71
71
 
72
72
  it { expect(limiter).not_to be_overloaded }
73
73
  it { expect(limiter).not_to be_inhibited }
@@ -1,23 +1,23 @@
1
1
  describe Berater::RateLimiter do
2
- before { Berater.mode = :rate }
2
+ it_behaves_like 'a limiter', Berater.new(:key, :rate, 3, :second)
3
3
 
4
4
  describe '.new' do
5
- let(:limiter) { described_class.new(1, :second) }
5
+ let(:limiter) { described_class.new(:key, 1, :second) }
6
6
 
7
7
  it 'initializes' do
8
+ expect(limiter.key).to be :key
8
9
  expect(limiter.count).to eq 1
9
- expect(limiter.interval).to eq 1
10
+ expect(limiter.interval).to eq :second
10
11
  end
11
12
 
12
13
  it 'has default values' do
13
- expect(limiter.key).to eq described_class.to_s
14
14
  expect(limiter.redis).to be Berater.redis
15
15
  end
16
16
  end
17
17
 
18
18
  describe '#count' do
19
19
  def expect_count(count)
20
- limiter = described_class.new(count, :second)
20
+ limiter = described_class.new(:key, count, :second)
21
21
  expect(limiter.count).to eq count
22
22
  end
23
23
 
@@ -28,7 +28,7 @@ describe Berater::RateLimiter do
28
28
  context 'with erroneous values' do
29
29
  def expect_bad_count(count)
30
30
  expect do
31
- described_class.new(count, :second)
31
+ described_class.new(:key, count, :second)
32
32
  end.to raise_error ArgumentError
33
33
  end
34
34
 
@@ -41,7 +41,7 @@ describe Berater::RateLimiter do
41
41
 
42
42
  describe '#interval' do
43
43
  def expect_interval(interval, expected)
44
- limiter = described_class.new(1, interval)
44
+ limiter = described_class.new(:key, 1, interval)
45
45
  expect(limiter.interval).to eq expected
46
46
  end
47
47
 
@@ -52,28 +52,28 @@ describe Berater::RateLimiter do
52
52
  end
53
53
 
54
54
  context 'with symbols' do
55
- it { expect_interval(:sec, 1) }
56
- it { expect_interval(:second, 1) }
57
- it { expect_interval(:seconds, 1) }
55
+ it { expect_interval(:sec, :second) }
56
+ it { expect_interval(:second, :second) }
57
+ it { expect_interval(:seconds, :second) }
58
58
 
59
- it { expect_interval(:min, 60) }
60
- it { expect_interval(:minute, 60) }
61
- it { expect_interval(:minutes, 60) }
59
+ it { expect_interval(:min, :minute) }
60
+ it { expect_interval(:minute, :minute) }
61
+ it { expect_interval(:minutes, :minute) }
62
62
 
63
- it { expect_interval(:hour, 3600) }
64
- it { expect_interval(:hours, 3600) }
63
+ it { expect_interval(:hour, :hour) }
64
+ it { expect_interval(:hours, :hour) }
65
65
  end
66
66
 
67
67
  context 'with strings' do
68
- it { expect_interval('sec', 1) }
69
- it { expect_interval('minute', 60) }
70
- it { expect_interval('hours', 3600) }
68
+ it { expect_interval('sec', :second) }
69
+ it { expect_interval('minute', :minute) }
70
+ it { expect_interval('hours', :hour) }
71
71
  end
72
72
 
73
73
  context 'with erroneous values' do
74
74
  def expect_bad_interval(interval)
75
75
  expect do
76
- described_class.new(1, interval)
76
+ described_class.new(:key, 1, interval)
77
77
  end.to raise_error(ArgumentError)
78
78
  end
79
79
 
@@ -81,85 +81,110 @@ describe Berater::RateLimiter do
81
81
  it { expect_bad_interval(:secondz) }
82
82
  it { expect_bad_interval('huor') }
83
83
  end
84
- end
85
84
 
86
- describe '#limit' do
87
- let(:limiter) { described_class.new(3, :second) }
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
88
90
 
89
- it 'works' do
90
- expect(limiter.limit).to eq 1
91
+ it { expect_sec(:second, 1) }
92
+ it { expect_sec(:minute, 60) }
93
+ it { expect_sec(:hour, 3600) }
91
94
  end
95
+ end
92
96
 
93
- it 'counts' do
94
- expect(limiter.limit).to eq 1
95
- expect(limiter.limit).to eq 2
96
- expect(limiter.limit).to eq 3
97
- end
97
+ describe '#limit' do
98
+ let(:limiter) { described_class.new(:key, 3, :second) }
98
99
 
99
- it 'yields' do
100
+ it 'works' do
100
101
  expect {|b| limiter.limit(&b) }.to yield_control
101
102
  expect(limiter.limit { 123 }).to eq 123
102
103
  end
103
104
 
105
+ it 'works without a block' do
106
+ expect(limiter.limit).to be_a Berater::Lock
107
+ end
108
+
104
109
  it 'limits excessive calls' do
105
110
  3.times { limiter.limit }
106
111
 
107
- expect { limiter.limit }.to be_overrated
112
+ expect(limiter).to be_overrated
108
113
  end
109
114
 
110
115
  it 'limit resets over time' do
111
- expect(limiter.limit).to eq 1
112
- expect(limiter.limit).to eq 2
113
- expect(limiter.limit).to eq 3
116
+ 3.times { limiter.limit }
114
117
  expect(limiter).to be_overrated
115
118
 
116
119
  # travel forward a second
117
120
  Timecop.freeze(1)
118
121
 
119
- expect(limiter.limit).to eq 1
120
- expect(limiter.limit).to eq 2
121
- expect(limiter.limit).to eq 3
122
+ 3.times { limiter.limit }
122
123
  expect(limiter).to be_overrated
123
124
  end
124
125
  end
125
126
 
126
127
  context 'with same key, different limiters' do
127
- let(:limiter_one) { described_class.new(1, :second) }
128
- let(:limiter_two) { described_class.new(1, :second) }
128
+ let(:limiter_one) { described_class.new(:key, 1, :second) }
129
+ let(:limiter_two) { described_class.new(:key, 1, :second) }
129
130
 
130
131
  it 'works as expected' do
131
- expect(limiter_one.limit).to eq 1
132
+ expect(limiter_one.limit).not_to be_overrated
132
133
 
133
- expect { limiter_one }.to be_overrated
134
- expect { limiter_two }.to be_overrated
134
+ expect(limiter_one).to be_overrated
135
+ expect(limiter_two).to be_overrated
135
136
  end
136
137
  end
137
138
 
138
- context 'with different keys, same limiter' do
139
- let(:limiter) { described_class.new(1, :second) }
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) }
140
142
 
141
143
  it 'works as expected' do
142
- expect { limiter.limit(key: :one) }.not_to be_overrated
143
- expect { limiter.limit(key: :one) }.to be_overrated
144
+ expect(limiter_one.limit).not_to be_overrated
145
+ expect(limiter_two.limit).not_to be_overrated
146
+
147
+ expect(limiter_one).to be_overrated
148
+ expect(limiter_two.limit).not_to be_overrated
144
149
 
145
- expect { limiter.limit(key: :two) }.not_to be_overrated
146
- expect { limiter.limit(key: :two) }.to be_overrated
150
+ expect(limiter_one).to be_overrated
151
+ expect(limiter_two).to be_overrated
147
152
  end
148
153
  end
149
154
 
150
- context 'with different keys, different limiters' do
151
- let(:limiter_one) { described_class.new(1, :second, key: :one) }
152
- let(:limiter_two) { described_class.new(2, :second, key: :two) }
155
+ describe '#to_s' do
156
+ def check(count, interval, expected)
157
+ expect(
158
+ described_class.new(:key, count, interval).to_s
159
+ ).to match(expected)
160
+ end
153
161
 
154
- it 'works as expected' do
155
- expect(limiter_one.limit).to eq 1
156
- expect(limiter_two.limit).to eq 1
162
+ it 'works with symbols' do
163
+ check(1, :second, /1 per second/)
164
+ check(1, :minute, /1 per minute/)
165
+ check(1, :hour, /1 per hour/)
166
+ end
167
+
168
+ it 'works with strings' do
169
+ check(1, 'second', /1 per second/)
170
+ check(1, 'minute', /1 per minute/)
171
+ check(1, 'hour', /1 per hour/)
172
+ end
157
173
 
158
- expect { limiter_one.limit }.to be_overrated
159
- expect(limiter_two.limit).to eq 2
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
160
183
 
161
- expect { limiter_one.limit }.to be_overrated
162
- expect { limiter_two.limit }.to be_overrated
184
+ it 'works with integers' do
185
+ check(1, 1, /1 every second/)
186
+ check(1, 2, /1 every 2 seconds/)
187
+ check(2, 3, /2 every 3 seconds/)
163
188
  end
164
189
  end
165
190
 
@@ -0,0 +1,183 @@
1
+ require 'berater/test_mode'
2
+
3
+ describe 'Berater.test_mode' do
4
+ after { Berater.test_mode = nil }
5
+
6
+ describe 'Unlimiter' do
7
+ let(:limiter) { Berater::Unlimiter.new }
8
+
9
+ context 'when test_mode = nil' do
10
+ before { Berater.test_mode = nil }
11
+
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
18
+ end
19
+
20
+ context 'when test_mode = :pass' do
21
+ before { Berater.test_mode = :pass }
22
+
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
29
+ end
30
+
31
+ context 'when test_mode = :fail' do
32
+ before { Berater.test_mode = :fail }
33
+
34
+ it { expect(limiter).to be_a Berater::Unlimiter }
35
+
36
+ it 'never works' do
37
+ expect { limiter }.to be_overloaded
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'Inhibitor' do
43
+ let(:limiter) { Berater::Inhibitor.new }
44
+
45
+ context 'when test_mode = nil' do
46
+ before { Berater.test_mode = nil }
47
+
48
+ it { expect(limiter).to be_a Berater::Inhibitor }
49
+
50
+ it 'works per usual' do
51
+ expect { limiter }.to be_overloaded
52
+ end
53
+ end
54
+
55
+ context 'when test_mode = :pass' do
56
+ before { Berater.test_mode = :pass }
57
+
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
64
+ end
65
+
66
+ context 'when test_mode = :fail' do
67
+ before { Berater.test_mode = :fail }
68
+
69
+ it { expect(limiter).to be_a Berater::Inhibitor }
70
+
71
+ it 'never works' do
72
+ expect { limiter }.to be_overloaded
73
+ end
74
+ end
75
+ end
76
+
77
+ describe 'RateLimiter' do
78
+ let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
79
+
80
+ shared_examples 'a RateLimiter' do
81
+ it { expect(limiter).to be_a Berater::RateLimiter }
82
+
83
+ it 'checks arguments' do
84
+ expect {
85
+ Berater::RateLimiter.new(:key, 1)
86
+ }.to raise_error(ArgumentError)
87
+ end
88
+ end
89
+
90
+ context 'when test_mode = nil' do
91
+ before { Berater.test_mode = nil }
92
+
93
+ it_behaves_like 'a RateLimiter'
94
+
95
+ it 'works per usual' do
96
+ expect(limiter.redis).to receive(:multi).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
103
+ end
104
+ end
105
+
106
+ context 'when test_mode = :pass' do
107
+ before { Berater.test_mode = :pass }
108
+
109
+ it_behaves_like 'a RateLimiter'
110
+
111
+ it 'always works and without calling redis' do
112
+ expect(limiter.redis).not_to receive(:multi)
113
+ expect {|block| limiter.limit(&block) }.to yield_control
114
+ 10.times { expect(limiter.limit).to be_a Berater::Lock }
115
+ end
116
+ end
117
+
118
+ context 'when test_mode = :fail' do
119
+ before { Berater.test_mode = :fail }
120
+
121
+ it_behaves_like 'a RateLimiter'
122
+
123
+ it 'never works and without calling redis' do
124
+ expect(limiter.redis).not_to receive(:multi)
125
+ expect { limiter }.to be_overloaded
126
+ end
127
+ end
128
+ end
129
+
130
+ describe 'ConcurrencyLimiter' do
131
+ let(:limiter) { Berater::ConcurrencyLimiter.new(:key, 1) }
132
+
133
+ shared_examples 'a ConcurrencyLimiter' do
134
+ it { expect(limiter).to be_a Berater::ConcurrencyLimiter }
135
+
136
+ it 'checks arguments' do
137
+ expect {
138
+ Berater::ConcurrencyLimiter.new(:key, 1.0)
139
+ }.to raise_error(ArgumentError)
140
+ end
141
+ end
142
+
143
+ context 'when test_mode = nil' do
144
+ before { Berater.test_mode = nil }
145
+
146
+ it_behaves_like 'a ConcurrencyLimiter'
147
+
148
+ 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
156
+ end
157
+ end
158
+
159
+ context 'when test_mode = :pass' do
160
+ before { Berater.test_mode = :pass }
161
+
162
+ 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
169
+ end
170
+
171
+ context 'when test_mode = :fail' do
172
+ before { Berater.test_mode = :fail }
173
+
174
+ 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
180
+ end
181
+ end
182
+
183
+ end