berater 0.0.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,202 @@
1
+ describe Berater::ConcurrencyLimiter do
2
+ before { Berater.mode = :concurrency }
3
+
4
+ describe '.new' do
5
+ let(:limiter) { described_class.new(1) }
6
+
7
+ it 'initializes' do
8
+ expect(limiter.capacity).to be 1
9
+ end
10
+
11
+ it 'has default values' do
12
+ expect(limiter.key).to eq described_class.to_s
13
+ expect(limiter.redis).to be Berater.redis
14
+ end
15
+ end
16
+
17
+ describe '#capacity' do
18
+ def expect_capacity(capacity)
19
+ limiter = described_class.new(capacity)
20
+ expect(limiter.capacity).to eq capacity
21
+ end
22
+
23
+ it { expect_capacity(0) }
24
+ it { expect_capacity(1) }
25
+ it { expect_capacity(10_000) }
26
+
27
+ context 'with erroneous values' do
28
+ def expect_bad_capacity(capacity)
29
+ expect do
30
+ described_class.new(capacity)
31
+ end.to raise_error ArgumentError
32
+ end
33
+
34
+ it { expect_bad_capacity(0.5) }
35
+ it { expect_bad_capacity(-1) }
36
+ it { expect_bad_capacity('1') }
37
+ it { expect_bad_capacity(:one) }
38
+ end
39
+ end
40
+
41
+ describe '#timeout' do
42
+ def expect_timeout(timeout)
43
+ limiter = described_class.new(1, timeout: timeout)
44
+ expect(limiter.timeout).to eq timeout
45
+ end
46
+
47
+ it { expect_timeout(0) }
48
+ it { expect_timeout(1) }
49
+ it { expect_timeout(10_000) }
50
+
51
+ context 'with erroneous values' do
52
+ def expect_bad_timeout(timeout)
53
+ expect do
54
+ described_class.new(1, timeout: timeout)
55
+ end.to raise_error ArgumentError
56
+ end
57
+
58
+ it { expect_bad_timeout(0.5) }
59
+ it { expect_bad_timeout(-1) }
60
+ it { expect_bad_timeout('1') }
61
+ it { expect_bad_timeout(:one) }
62
+ end
63
+ end
64
+
65
+ describe '#limit' do
66
+ let(:limiter) { described_class.new(2, timeout: 1) }
67
+
68
+ it 'works' do
69
+ expect {|b| limiter.limit(&b) }.to yield_control
70
+ end
71
+
72
+ it 'works many times if workers release locks' do
73
+ 30.times do
74
+ expect {|b| limiter.limit(&b) }.to yield_control
75
+ end
76
+
77
+ 30.times do
78
+ lock = limiter.limit
79
+ lock.release
80
+ end
81
+ end
82
+
83
+ it 'limits excessive calls' do
84
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
85
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
86
+
87
+ expect(limiter).to be_incapacitated
88
+ end
89
+
90
+ it 'times out locks' do
91
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
92
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
93
+ expect(limiter).to be_incapacitated
94
+
95
+ Timecop.travel(1)
96
+
97
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
98
+ expect(limiter.limit).to be_a Berater::ConcurrencyLimiter::Lock
99
+ expect(limiter).to be_incapacitated
100
+ end
101
+
102
+ context 'with capacity 0' do
103
+ let(:limiter) { described_class.new(0) }
104
+
105
+ it 'always fails' do
106
+ expect(limiter).to be_incapacitated
107
+ end
108
+ end
109
+ end
110
+
111
+ context 'with same key, different limiters' do
112
+ let(:limiter_one) { described_class.new(1) }
113
+ let(:limiter_two) { described_class.new(1) }
114
+
115
+ it { expect(limiter_one.key).to eq limiter_two.key }
116
+
117
+ it 'works as expected' do
118
+ expect(limiter_one.limit).to be_a Berater::ConcurrencyLimiter::Lock
119
+
120
+ expect(limiter_one).to be_incapacitated
121
+ expect(limiter_two).to be_incapacitated
122
+ end
123
+ end
124
+
125
+ context 'with different keys, same limiter' do
126
+ let(:limiter) { described_class.new(1) }
127
+
128
+ it 'works as expected' do
129
+ one_lock = limiter.limit(key: :one)
130
+ expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
131
+
132
+ expect { limiter.limit(key: :one) {} }.to be_incapacitated
133
+ expect { limiter.limit(key: :two) {} }.not_to be_incapacitated
134
+
135
+ two_lock = limiter.limit(key: :two)
136
+ expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
137
+
138
+ expect { limiter.limit(key: :one) {} }.to be_incapacitated
139
+ expect { limiter.limit(key: :two) {} }.to be_incapacitated
140
+
141
+ one_lock.release
142
+ expect { limiter.limit(key: :one) {} }.not_to be_incapacitated
143
+ expect { limiter.limit(key: :two) {} }.to be_incapacitated
144
+
145
+ two_lock.release
146
+ expect { limiter.limit(key: :one) {} }.not_to be_incapacitated
147
+ expect { limiter.limit(key: :two) {} }.not_to be_incapacitated
148
+ end
149
+ end
150
+
151
+ context 'with same key, different capacities' do
152
+ let(:limiter_one) { described_class.new(1) }
153
+ let(:limiter_two) { described_class.new(2) }
154
+
155
+ it { expect(limiter_one.capacity).not_to eq limiter_two.capacity }
156
+
157
+ it 'works as expected' do
158
+ one_lock = limiter_one.limit
159
+ expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
160
+
161
+ expect(limiter_one).to be_incapacitated
162
+ expect(limiter_two).not_to be_incapacitated
163
+
164
+ two_lock = limiter_two.limit
165
+ expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
166
+
167
+ expect(limiter_one).to be_incapacitated
168
+ expect(limiter_two).to be_incapacitated
169
+
170
+ one_lock.release
171
+ expect(limiter_one).to be_incapacitated
172
+ expect(limiter_two).not_to be_incapacitated
173
+
174
+ two_lock.release
175
+ expect(limiter_one).not_to be_incapacitated
176
+ expect(limiter_two).not_to be_incapacitated
177
+ end
178
+ end
179
+
180
+ context 'with different keys, different limiters' do
181
+ let(:limiter_one) { described_class.new(1, key: :one) }
182
+ let(:limiter_two) { described_class.new(1, key: :two) }
183
+
184
+ it 'works as expected' do
185
+ expect(limiter_one).not_to be_incapacitated
186
+ expect(limiter_two).not_to be_incapacitated
187
+
188
+ one_lock = limiter_one.limit
189
+ expect(one_lock).to be_a Berater::ConcurrencyLimiter::Lock
190
+
191
+ expect(limiter_one).to be_incapacitated
192
+ expect(limiter_two).not_to be_incapacitated
193
+
194
+ two_lock = limiter_two.limit
195
+ expect(two_lock).to be_a Berater::ConcurrencyLimiter::Lock
196
+
197
+ expect(limiter_one).to be_incapacitated
198
+ expect(limiter_two).to be_incapacitated
199
+ end
200
+ end
201
+
202
+ end
@@ -0,0 +1,92 @@
1
+ describe Berater::ConcurrencyLimiter::Lock do
2
+ before { Berater.mode = :concurrency }
3
+
4
+ let(:limiter) { Berater.new(:concurrency, 3) }
5
+
6
+ describe '#contention' do
7
+ it 'tracks contention' do
8
+ lock_1 = limiter.limit
9
+ expect(lock_1.contention).to eq 1
10
+
11
+ lock_2 = limiter.limit
12
+ expect(lock_2.contention).to eq 2
13
+
14
+ limiter.limit do |lock_3|
15
+ expect(lock_3.contention).to eq 3
16
+ end
17
+ end
18
+
19
+ it 'works in block mode' do
20
+ lock_1 = limiter.limit
21
+
22
+ limiter.limit do |lock_2|
23
+ expect(lock_1.contention).to eq 1
24
+ expect(lock_2.contention).to eq 2
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#release' do
30
+ it 'can not be released twice' do
31
+ lock = limiter.limit
32
+ expect(lock.release).to be true
33
+ expect { lock.release }.to raise_error(RuntimeError, /already/)
34
+ end
35
+
36
+ it 'does not work in block mode' do
37
+ expect do
38
+ limiter.limit do |lock|
39
+ lock.release
40
+ end
41
+ end.to raise_error(RuntimeError, /already/)
42
+ end
43
+ end
44
+
45
+ describe '#released?' do
46
+ it 'works' do
47
+ lock = limiter.limit
48
+ expect(lock.released?).to be false
49
+
50
+ lock.release
51
+ expect(lock.released?).to be true
52
+ end
53
+
54
+ it 'works in block mode' do
55
+ limiter.limit do |lock|
56
+ expect(lock.released?).to be false
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#expired?' do
62
+ let!(:lock) { limiter.limit }
63
+
64
+ context 'when timeout is not set' do
65
+ it { expect(limiter.timeout).to eq 0 }
66
+
67
+ it 'never expires' do
68
+ expect(lock.expired?).to be false
69
+
70
+ Timecop.travel(1_000)
71
+
72
+ expect(lock.expired?).to be false
73
+ end
74
+ end
75
+
76
+ context 'when timeout is set and exceeded' do
77
+ before { Timecop.travel(1) }
78
+
79
+ let(:limiter) { Berater.new(:concurrency, 3, timeout: 1) }
80
+
81
+ it 'expires' do
82
+ expect(lock.expired?).to be true
83
+ end
84
+
85
+ it 'fails to release' do
86
+ expect(lock.released?).to be false
87
+ expect { lock.release }.to raise_error(RuntimeError, /expired/)
88
+ end
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,37 @@
1
+ describe Berater::Inhibitor do
2
+ before { Berater.mode = :inhibited }
3
+
4
+ describe '.new' do
5
+ it 'initializes without any arguments or options' do
6
+ expect(described_class.new).to be_a described_class
7
+ end
8
+
9
+ it 'initializes with any arguments and options' do
10
+ expect(described_class.new(:abc, x: 123)).to be_a described_class
11
+ end
12
+
13
+ it 'has default values' do
14
+ expect(described_class.new.key).to eq described_class.to_s
15
+ expect(described_class.new.redis).to be Berater.redis
16
+ end
17
+ end
18
+
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
+ describe '#limit' do
30
+ let(:limiter) { described_class.new }
31
+
32
+ it 'always limits' do
33
+ expect { described_class.limit }.to be_inhibited
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,118 @@
1
+ describe 'be_overloaded' do
2
+ context 'Berater::Unlimiter' do
3
+ let(:limiter) { Berater.new(:unlimited) }
4
+
5
+ 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
+ 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
+ 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
+ end
20
+
21
+ context 'Berater::Inhibitor' do
22
+ let(:limiter) { Berater.new(:inhibited) }
23
+
24
+ it { expect(limiter).to be_overloaded }
25
+ it { expect(limiter).to be_inhibited }
26
+
27
+ it { expect { limiter }.to be_overloaded }
28
+ it { expect { limiter }.to be_inhibited }
29
+
30
+ it { expect { limiter.limit }.to be_overloaded }
31
+ it { expect { limiter.limit }.to be_inhibited }
32
+ end
33
+
34
+ context 'Berater::RateLimiter' do
35
+ let(:limiter) { Berater.new(:rate, 1, :second) }
36
+
37
+ it { expect(limiter).not_to be_overloaded }
38
+ it { expect(limiter).not_to be_inhibited }
39
+ it { expect(limiter).not_to be_overrated }
40
+ it { expect(limiter).not_to be_incapacitated }
41
+
42
+ it { expect { limiter }.not_to be_overloaded }
43
+ it { expect { limiter }.not_to be_inhibited }
44
+ it { expect { limiter }.not_to be_overrated }
45
+ it { expect { limiter }.not_to be_incapacitated }
46
+
47
+ it { expect { limiter.limit }.not_to be_overloaded }
48
+ it { expect { limiter.limit }.not_to be_inhibited }
49
+ it { expect { limiter.limit }.not_to be_overrated }
50
+ it { expect { limiter.limit }.not_to be_incapacitated }
51
+
52
+ context 'once limit is used up' do
53
+ before { limiter.limit }
54
+
55
+ it 'should be_overrated' do
56
+ expect(limiter).to be_overrated
57
+ end
58
+
59
+ it 'should be_overrated' do
60
+ expect { limiter }.to be_overrated
61
+ end
62
+
63
+ it 'should be_overrated' do
64
+ expect { limiter.limit }.to be_overrated
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'Berater::ConcurrencyLimiter' do
70
+ let(:limiter) { Berater.new(:concurrency, 1) }
71
+
72
+ it { expect(limiter).not_to be_overloaded }
73
+ it { expect(limiter).not_to be_inhibited }
74
+ it { expect(limiter).not_to be_overrated }
75
+ it { expect(limiter).not_to be_incapacitated }
76
+
77
+ it { expect { limiter }.not_to be_overloaded }
78
+ it { expect { limiter }.not_to be_inhibited }
79
+ it { expect { limiter }.not_to be_overrated }
80
+ it { expect { limiter }.not_to be_incapacitated }
81
+
82
+ it { expect { limiter.limit }.not_to be_overloaded }
83
+ it { expect { limiter.limit }.not_to be_inhibited }
84
+ it { expect { limiter.limit }.not_to be_overrated }
85
+ it { expect { limiter.limit }.not_to be_incapacitated }
86
+
87
+ context 'when lock is released' do
88
+ it 'should be_incapacitated' do
89
+ 3.times do
90
+ expect(limiter).not_to be_incapacitated
91
+ end
92
+ end
93
+
94
+ it 'should be_incapacitated' do
95
+ 3.times do
96
+ expect { limiter }.not_to be_incapacitated
97
+ end
98
+ end
99
+
100
+ it 'should be_incapacitated' do
101
+ 3.times do
102
+ expect { limiter.limit {} }.not_to be_incapacitated
103
+ end
104
+ end
105
+ end
106
+
107
+ context 'when lock is *not* released' do
108
+ it 'should be_incapacitated' do
109
+ expect { limiter.limit }.not_to be_incapacitated
110
+ expect { limiter.limit }.to be_incapacitated
111
+ end
112
+
113
+ it 'should be_incapacitated' do
114
+ expect { 3.times { limiter.limit } }.to be_incapacitated
115
+ end
116
+ end
117
+ end
118
+ end