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.
@@ -0,0 +1,46 @@
1
+ require 'berater/dsl'
2
+
3
+ describe Berater do
4
+ using Berater::DSL
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
+ it 'instatiates a RateLimiter' do
19
+ limiter = Berater.new(:key) { 1.per second }
20
+ expect(limiter).to be_a Berater::RateLimiter
21
+ expect(limiter.key).to be :key
22
+ expect(limiter.capacity).to be 1
23
+ expect(limiter.interval).to be :second
24
+ end
25
+
26
+ it 'instatiates a ConcurrencyLimiter' do
27
+ limiter = Berater.new(:key, timeout: 2) { 1.at_once }
28
+ expect(limiter).to be_a Berater::ConcurrencyLimiter
29
+ expect(limiter.key).to be :key
30
+ expect(limiter.capacity).to be 1
31
+ expect(limiter.timeout).to be 2
32
+ end
33
+
34
+ it 'does not accept args and dsl block' do
35
+ expect {
36
+ Berater.new(:key, 2) { 3.at_once }
37
+ }.to raise_error(ArgumentError)
38
+ end
39
+
40
+ it 'requires either mode or dsl block' do
41
+ expect {
42
+ Berater.new(:key)
43
+ }.to raise_error(ArgumentError)
44
+ end
45
+
46
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,72 @@
1
+ require 'berater/dsl'
2
+
3
+ describe Berater::DSL do
4
+ def check(expected, &block)
5
+ expect(Berater::DSL.eval(&block)).to eq expected
6
+ end
7
+
8
+ context 'rate mode' do
9
+ it 'has keywords' do
10
+ check(:second) { second }
11
+ check(:minute) { minute }
12
+ check(:hour) { hour }
13
+ end
14
+
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 }
19
+ end
20
+
21
+ it 'cleans up afterward' do
22
+ check([ 1, :second ]) { 1.per second }
23
+
24
+ expect(Integer).not_to respond_to(:per)
25
+ expect(Integer).not_to respond_to(:every)
26
+ end
27
+
28
+ it 'works with variables' do
29
+ count = 1
30
+ interval = :second
31
+
32
+ check([ count, interval ]) { count.per interval }
33
+ end
34
+ end
35
+
36
+ context 'concurrency mode' do
37
+ it 'parses' do
38
+ check([ 1 ]) { 1.at_once }
39
+ check([ 3 ]) { 3.at_a_time }
40
+ check([ 5 ]) { 5.concurrently }
41
+ end
42
+
43
+ it 'cleans up afterward' do
44
+ check([ 1 ]) { 1.at_once }
45
+
46
+ expect(Integer).not_to respond_to(:at_once)
47
+ expect(Integer).not_to respond_to(:at_a_time)
48
+ expect(Integer).not_to respond_to(:concurrently)
49
+ end
50
+
51
+ it 'works with constants' do
52
+ class Foo
53
+ CAPACITY = 3
54
+ end
55
+
56
+ check([ Foo::CAPACITY ]) { Foo::CAPACITY.at_once }
57
+ end
58
+ end
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
+ end
@@ -1,37 +1,23 @@
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
- let(:limiter) { described_class.new }
18
+ subject { described_class.new }
31
19
 
32
- it 'always limits' do
33
- expect { described_class.limit }.to be_inhibited
34
- end
20
+ it_behaves_like 'it is overloaded'
35
21
  end
36
22
 
37
23
  end
@@ -0,0 +1,71 @@
1
+ describe Berater::Limiter do
2
+ it 'can not be initialized' do
3
+ expect { described_class.new }.to raise_error(NotImplementedError)
4
+ end
5
+
6
+ describe 'abstract methods' do
7
+ let(:limiter) { Class.new(described_class).new(:key, 1) }
8
+
9
+ it do
10
+ expect { limiter.limit }.to raise_error(NotImplementedError)
11
+ expect { limiter.overloaded? }.to raise_error(NotImplementedError)
12
+ end
13
+ end
14
+
15
+ describe '==' do
16
+ let(:limiter) { Berater::RateLimiter.new(:key, 1, :second) }
17
+
18
+ it 'equals itself' do
19
+ expect(limiter).to eq limiter
20
+ end
21
+
22
+ it 'equals something with the same initialization parameters' do
23
+ expect(limiter).to eq(
24
+ Berater::RateLimiter.new(:key, 1, :second)
25
+ )
26
+ end
27
+
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
+ it 'does not equal something different' do
35
+ expect(limiter).not_to eq(
36
+ Berater::RateLimiter.new(:key, 2, :second)
37
+ )
38
+
39
+ expect(limiter).not_to eq(
40
+ Berater::RateLimiter.new(:keyz, 1, :second)
41
+ )
42
+
43
+ expect(limiter).not_to eq(
44
+ Berater::RateLimiter.new(:key, 1, :minute)
45
+ )
46
+ end
47
+
48
+ it 'does not equal something altogether different' do
49
+ expect(limiter).not_to eq(
50
+ Berater::ConcurrencyLimiter.new(:key, 1)
51
+ )
52
+ end
53
+
54
+ it 'works for ConcurrencyLimiter too' do
55
+ limiter = Berater::ConcurrencyLimiter.new(:key, 1)
56
+ expect(limiter).to eq limiter
57
+
58
+ expect(limiter).not_to eq(
59
+ Berater::ConcurrencyLimiter.new(:key, 1, timeout: 1)
60
+ )
61
+ end
62
+
63
+ it 'and the others' do
64
+ unlimiter = Berater::Unlimiter.new
65
+ expect(unlimiter).to eq unlimiter
66
+
67
+ expect(unlimiter).not_to eq Berater::Inhibitor.new
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,97 @@
1
+ describe Berater::LuaScript do
2
+ subject { Berater::LuaScript('return redis.call("PING")') }
3
+
4
+ before { redis.script(:flush) }
5
+
6
+ let(:redis) { Berater.redis }
7
+
8
+ it { is_expected.to be_a Berater::LuaScript }
9
+
10
+ describe '#eval' do
11
+ def ping
12
+ expect(subject.eval(redis)).to eq 'PONG'
13
+ end
14
+
15
+ it { ping }
16
+
17
+ it 'loads the script into redis' do
18
+ expect(redis).to receive(:evalsha).once.and_call_original
19
+ expect(redis).to receive(:eval).once.and_call_original
20
+ ping
21
+ expect(subject.loaded?(redis)).to be true
22
+ end
23
+ end
24
+
25
+ describe '#load' do
26
+ it 'loads script into redis' do
27
+ expect(redis.script(:exists, subject.sha)).to be false
28
+ subject.load(redis)
29
+ expect(redis.script(:exists, subject.sha)).to be true
30
+ end
31
+
32
+ it 'returns the sha' do
33
+ expect(subject.load(redis)).to eq subject.sha
34
+ end
35
+
36
+ it 'validates the returned sha' do
37
+ allow(redis).to receive(:script).with(:flush).and_call_original
38
+ expect(redis).to receive(:script).with(:load, String).and_return('abc')
39
+ expect { subject.load(redis) }.to raise_error(RuntimeError)
40
+ end
41
+ end
42
+
43
+ describe '#loaded?' do
44
+ it do
45
+ expect(subject.loaded?(redis)).to be false
46
+ subject.load(redis)
47
+ expect(subject.loaded?(redis)).to be true
48
+ end
49
+ end
50
+
51
+ describe '#to_s' do
52
+ it { expect(subject.to_s).to be subject.source }
53
+ end
54
+
55
+ describe '#minify' do
56
+ subject do
57
+ expect(
58
+ Berater::LuaScript(lua).send(:minify)
59
+ ).to eq expected
60
+ end
61
+
62
+ context do
63
+ let(:lua) do <<-LUA
64
+ -- this comment gets removed
65
+ redis.call('PING') -- this one too
66
+ LUA
67
+ end
68
+
69
+ let(:expected) { "redis.call('PING')" }
70
+
71
+ it { subject }
72
+ end
73
+
74
+ context 'with if statement' do
75
+ let(:lua) do <<~LUA
76
+ if condition then
77
+ call
78
+ end
79
+
80
+ return 123
81
+ LUA
82
+ end
83
+
84
+ let(:expected) do
85
+ [
86
+ 'if condition then',
87
+ 'call',
88
+ 'end',
89
+ 'return 123'
90
+ ].join "\n"
91
+ end
92
+
93
+ it { subject }
94
+ end
95
+ end
96
+
97
+ end
@@ -1,6 +1,7 @@
1
- describe 'be_overloaded' do
1
+ describe Berater::Matchers::Overloaded do
2
+
2
3
  context 'Berater::Unlimiter' do
3
- let(:limiter) { Berater.new(:unlimited) }
4
+ let(:limiter) { Berater.new(:key, :unlimited) }
4
5
 
5
6
  it { expect(limiter).not_to be_overloaded }
6
7
  it { expect(limiter).not_to be_inhibited }
@@ -19,7 +20,7 @@ describe 'be_overloaded' do
19
20
  end
20
21
 
21
22
  context 'Berater::Inhibitor' do
22
- let(:limiter) { Berater.new(:inhibited) }
23
+ let(:limiter) { Berater.new(:key, :inhibited) }
23
24
 
24
25
  it { expect(limiter).to be_overloaded }
25
26
  it { expect(limiter).to be_inhibited }
@@ -32,7 +33,7 @@ describe 'be_overloaded' do
32
33
  end
33
34
 
34
35
  context 'Berater::RateLimiter' do
35
- let(:limiter) { Berater.new(:rate, 1, :second) }
36
+ let(:limiter) { Berater.new(:key, 1, :second) }
36
37
 
37
38
  it { expect(limiter).not_to be_overloaded }
38
39
  it { expect(limiter).not_to be_inhibited }
@@ -67,7 +68,7 @@ describe 'be_overloaded' do
67
68
  end
68
69
 
69
70
  context 'Berater::ConcurrencyLimiter' do
70
- let(:limiter) { Berater.new(:concurrency, 1) }
71
+ let(:limiter) { Berater.new(:key, 1) }
71
72
 
72
73
  it { expect(limiter).not_to be_overloaded }
73
74
  it { expect(limiter).not_to be_inhibited }
@@ -115,4 +116,71 @@ describe 'be_overloaded' do
115
116
  end
116
117
  end
117
118
  end
119
+
120
+ context 'when matchers fail' do
121
+ let(:unlimiter) { Berater::Unlimiter.new }
122
+ let(:inhibitor) { Berater::Inhibitor.new }
123
+
124
+ it 'catches false negatives' do
125
+ expect {
126
+ expect(unlimiter).to be_overloaded
127
+ }.to fail_including('expected to be overloaded')
128
+
129
+ expect {
130
+ expect { unlimiter }.to be_overloaded
131
+ }.to fail_including('expected to be overloaded')
132
+
133
+ expect {
134
+ expect { unlimiter.limit }.to be_overloaded
135
+ }.to fail_including("expected #{Berater::Overloaded} to be raised")
136
+
137
+ expect {
138
+ expect { 123 }.to be_overloaded
139
+ }.to fail_including("expected #{Berater::Overloaded} to be raised")
140
+ end
141
+
142
+ it 'catches false positives' do
143
+ expect {
144
+ expect(inhibitor).not_to be_overloaded
145
+ }.to fail_including('expected not to be overloaded')
146
+
147
+ expect {
148
+ expect { inhibitor }.not_to be_overloaded
149
+ }.to fail_including('expected not to be overloaded')
150
+
151
+ expect {
152
+ expect { inhibitor.limit }.not_to be_overloaded
153
+ }.to fail_including("did not expect #{Berater::Overloaded} to be raised")
154
+
155
+ expect {
156
+ expect { raise Berater::Overloaded }.not_to be_overloaded
157
+ }.to fail_including("did not expect #{Berater::Overloaded} to be raised")
158
+ 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
+ end
118
186
  end
@@ -1,165 +1,228 @@
1
1
  describe Berater::RateLimiter do
2
- before { Berater.mode = :rate }
2
+ it_behaves_like 'a limiter', Berater.new(:key, 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.count).to eq 1
9
- expect(limiter.interval).to eq 1
8
+ expect(limiter.key).to be :key
9
+ expect(limiter.capacity).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
- describe '#count' do
19
- def expect_count(count)
20
- limiter = described_class.new(count, :second)
21
- expect(limiter.count).to eq count
18
+ describe '#capacity' do
19
+ def expect_capacity(capacity)
20
+ limiter = described_class.new(:key, capacity, :second)
21
+ expect(limiter.capacity).to eq capacity
22
22
  end
23
23
 
24
- it { expect_count(0) }
25
- it { expect_count(1) }
26
- it { expect_count(100) }
24
+ it { expect_capacity(0) }
25
+ it { expect_capacity(1) }
26
+ it { expect_capacity(1.5) }
27
+ it { expect_capacity(100) }
27
28
 
28
29
  context 'with erroneous values' do
29
- def expect_bad_count(count)
30
+ def expect_bad_capacity(capacity)
30
31
  expect do
31
- described_class.new(count, :second)
32
+ described_class.new(:key, capacity, :second)
32
33
  end.to raise_error ArgumentError
33
34
  end
34
35
 
35
- it { expect_bad_count(0.5) }
36
- it { expect_bad_count(-1) }
37
- it { expect_bad_count('1') }
38
- it { expect_bad_count(:one) }
36
+ it { expect_bad_capacity(-1) }
37
+ it { expect_bad_capacity('1') }
38
+ it { expect_bad_capacity(:one) }
39
39
  end
40
40
  end
41
41
 
42
42
  describe '#interval' do
43
- def expect_interval(interval, expected)
44
- limiter = described_class.new(1, interval)
45
- expect(limiter.interval).to eq expected
46
- end
47
-
48
- context 'with ints' do
49
- it { expect_interval(0, 0) }
50
- it { expect_interval(1, 1) }
51
- it { expect_interval(33, 33) }
52
- end
53
-
54
- context 'with symbols' do
55
- it { expect_interval(:sec, 1) }
56
- it { expect_interval(:second, 1) }
57
- it { expect_interval(:seconds, 1) }
43
+ # see spec/utils_spec.rb for more
58
44
 
59
- it { expect_interval(:min, 60) }
60
- it { expect_interval(:minute, 60) }
61
- it { expect_interval(:minutes, 60) }
45
+ subject { described_class.new(:key, 1, :second) }
62
46
 
63
- it { expect_interval(:hour, 3600) }
64
- it { expect_interval(:hours, 3600) }
47
+ it 'saves the interval in original and millisecond format' do
48
+ expect(subject.interval).to be :second
49
+ expect(subject.instance_variable_get(:@interval_msec)).to be 10**3
65
50
  end
66
51
 
67
- context 'with strings' do
68
- it { expect_interval('sec', 1) }
69
- it { expect_interval('minute', 60) }
70
- it { expect_interval('hours', 3600) }
71
- end
52
+ it 'must be > 0' do
53
+ expect {
54
+ described_class.new(:key, 1, 0)
55
+ }.to raise_error(ArgumentError)
72
56
 
73
- context 'with erroneous values' do
74
- def expect_bad_interval(interval)
75
- expect do
76
- described_class.new(1, interval)
77
- end.to raise_error(ArgumentError)
78
- end
79
-
80
- it { expect_bad_interval(-1) }
81
- it { expect_bad_interval(:secondz) }
82
- it { expect_bad_interval('huor') }
57
+ expect {
58
+ described_class.new(:key, 1, -1)
59
+ }.to raise_error(ArgumentError)
83
60
  end
84
61
  end
85
62
 
86
63
  describe '#limit' do
87
- let(:limiter) { described_class.new(3, :second) }
64
+ let(:limiter) { described_class.new(:key, 3, :second) }
88
65
 
89
66
  it 'works' do
90
- expect(limiter.limit).to eq 1
91
- end
92
-
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
98
-
99
- it 'yields' do
100
67
  expect {|b| limiter.limit(&b) }.to yield_control
101
68
  expect(limiter.limit { 123 }).to eq 123
102
69
  end
103
70
 
71
+ it 'works without a block' do
72
+ expect(limiter.limit).to be_a Berater::Lock
73
+ end
74
+
104
75
  it 'limits excessive calls' do
105
76
  3.times { limiter.limit }
106
77
 
107
- expect { limiter.limit }.to be_overrated
78
+ expect(limiter).to be_overrated
108
79
  end
109
80
 
110
- 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
81
+ it 'resets limit over time' do
82
+ 3.times { limiter.limit }
114
83
  expect(limiter).to be_overrated
115
84
 
116
- # travel forward a second
117
85
  Timecop.freeze(1)
118
86
 
119
- expect(limiter.limit).to eq 1
120
- expect(limiter.limit).to eq 2
121
- expect(limiter.limit).to eq 3
87
+ 3.times { limiter.limit }
122
88
  expect(limiter).to be_overrated
123
89
  end
124
- end
125
90
 
126
- 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) }
91
+ context 'with millisecond precision' do
92
+ it 'resets limit over time' do
93
+ 3.times { limiter.limit }
94
+ expect(limiter).to be_overrated
95
+
96
+ # travel forward to just before the count decrements
97
+ Timecop.freeze(0.333)
98
+ expect(limiter).to be_overrated
99
+
100
+ # traveling one more millisecond will decrement the count
101
+ Timecop.freeze(0.001)
102
+ limiter.limit
103
+ expect(limiter).to be_overrated
104
+ end
105
+
106
+ it 'works when drip rate is < 1 per millisecond' do
107
+ limiter = described_class.new(:key, 2_000, :second)
129
108
 
130
- it 'works as expected' do
131
- expect(limiter_one.limit).to eq 1
109
+ limiter.capacity.times { limiter.limit }
110
+ expect(limiter).to be_overrated
132
111
 
133
- expect { limiter_one }.to be_overrated
134
- expect { limiter_two }.to be_overrated
112
+ Timecop.freeze(0.001)
113
+ expect(limiter).not_to be_overrated
114
+
115
+ 2.times { limiter.limit }
116
+ end
135
117
  end
136
- end
137
118
 
138
- context 'with different keys, same limiter' do
139
- let(:limiter) { described_class.new(1, :second) }
119
+ context 'when capacity is a Float' do
120
+ let(:limiter) { described_class.new(:key, 1.5, :second) }
121
+
122
+ it 'still works' do
123
+ limiter.limit
124
+ expect(limiter).not_to be_overrated
125
+
126
+ expect { limiter.limit }.to be_overrated
127
+
128
+ limiter.limit(cost: 0.5)
129
+ end
130
+ end
131
+
132
+ it 'accepts a dynamic capacity' do
133
+ limiter = described_class.new(:key, 1, :second)
134
+
135
+ expect { limiter.limit(capacity: 0) }.to be_overrated
136
+ 5.times { limiter.limit(capacity: 10) }
137
+ expect { limiter }.to be_overrated
138
+ end
139
+
140
+ context 'works with cost parameter' do
141
+ it { expect { limiter.limit(cost: 4) }.to be_overrated }
142
+
143
+ it 'works within limit' do
144
+ limiter.limit(cost: 3)
145
+ expect { limiter.limit }.to be_overrated
146
+ end
147
+
148
+ it 'resets over time' do
149
+ limiter.limit(cost: 3)
150
+ expect(limiter).to be_overrated
151
+
152
+ Timecop.freeze(1)
153
+ expect(limiter).not_to be_overrated
154
+ end
155
+
156
+ it 'can be a Float' do
157
+ 2.times { limiter.limit(cost: 1.5) }
158
+ expect(limiter).to be_overrated
159
+ end
160
+ end
161
+
162
+ context 'with same key, different limiters' do
163
+ let(:limiter_one) { described_class.new(:key, 1, :second) }
164
+ let(:limiter_two) { described_class.new(:key, 1, :second) }
165
+
166
+ it 'works as expected' do
167
+ expect(limiter_one.limit).not_to be_overrated
140
168
 
141
- it 'works as expected' do
142
- expect { limiter.limit(key: :one) }.not_to be_overrated
143
- expect { limiter.limit(key: :one) }.to be_overrated
169
+ expect(limiter_one).to be_overrated
170
+ expect(limiter_two).to be_overrated
171
+ end
172
+ end
173
+
174
+ context 'with different keys, different limiters' do
175
+ let(:limiter_one) { described_class.new(:one, 1, :second) }
176
+ let(:limiter_two) { described_class.new(:two, 2, :second) }
177
+
178
+ it 'works as expected' do
179
+ expect(limiter_one.limit).not_to be_overrated
180
+ expect(limiter_two.limit).not_to be_overrated
144
181
 
145
- expect { limiter.limit(key: :two) }.not_to be_overrated
146
- expect { limiter.limit(key: :two) }.to be_overrated
182
+ expect(limiter_one).to be_overrated
183
+ expect(limiter_two.limit).not_to be_overrated
184
+
185
+ expect(limiter_one).to be_overrated
186
+ expect(limiter_two).to be_overrated
187
+ end
147
188
  end
148
189
  end
149
190
 
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) }
191
+ describe '#overloaded?' do
192
+ let(:limiter) { described_class.new(:key, 1, :second) }
153
193
 
154
- it 'works as expected' do
155
- expect(limiter_one.limit).to eq 1
156
- expect(limiter_two.limit).to eq 1
194
+ it 'works' do
195
+ expect(limiter.overloaded?).to be false
196
+ limiter.limit
197
+ expect(limiter.overloaded?).to be true
198
+ Timecop.freeze(1)
199
+ expect(limiter.overloaded?).to be false
200
+ end
201
+ end
202
+
203
+ describe '#to_s' do
204
+ def check(capacity, interval, expected)
205
+ expect(
206
+ described_class.new(:key, capacity, interval).to_s
207
+ ).to match(expected)
208
+ end
157
209
 
158
- expect { limiter_one.limit }.to be_overrated
159
- expect(limiter_two.limit).to eq 2
210
+ it 'works with symbols' do
211
+ check(1, :second, /1 per second/)
212
+ check(1, :minute, /1 per minute/)
213
+ check(1, :hour, /1 per hour/)
214
+ end
215
+
216
+ it 'works with strings' do
217
+ check(1, 'second', /1 per second/)
218
+ check(1, 'minute', /1 per minute/)
219
+ check(1, 'hour', /1 per hour/)
220
+ end
160
221
 
161
- expect { limiter_one.limit }.to be_overrated
162
- expect { limiter_two.limit }.to be_overrated
222
+ it 'works with integers' do
223
+ check(1, 1, /1 every second/)
224
+ check(1, 2, /1 every 2 seconds/)
225
+ check(2, 3, /2 every 3 seconds/)
163
226
  end
164
227
  end
165
228