pause 0.4.0 → 0.5.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +31 -0
- data/.github/workflows/rubocop.yml +31 -0
- data/.gitignore +0 -1
- data/.rubocop.yml +35 -0
- data/.rubocop_todo.yml +192 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +15 -1
- data/Gemfile.lock +96 -0
- data/LICENSE.txt +1 -1
- data/README.md +94 -48
- data/Rakefile +11 -10
- data/bin/spec +14 -0
- data/lib/pause/action.rb +7 -6
- data/lib/pause/analyzer.rb +4 -0
- data/lib/pause/configuration.rb +3 -1
- data/lib/pause/helper/timing.rb +2 -0
- data/lib/pause/logger.rb +12 -5
- data/lib/pause/rate_limited_event.rb +2 -1
- data/lib/pause/redis/adapter.rb +12 -10
- data/lib/pause/redis/sharded_adapter.rb +3 -2
- data/lib/pause/version.rb +3 -1
- data/lib/pause.rb +25 -14
- data/pause.gemspec +14 -17
- data/spec/pause/action_spec.rb +48 -42
- data/spec/pause/analyzer_spec.rb +13 -10
- data/spec/pause/configuration_spec.rb +6 -5
- data/spec/pause/logger_spec.rb +25 -0
- data/spec/pause/pause_spec.rb +6 -4
- data/spec/pause/period_check_spec.rb +53 -0
- data/spec/pause/redis/adapter_spec.rb +54 -35
- data/spec/pause/redis/sharded_adapter_spec.rb +10 -10
- data/spec/spec_helper.rb +11 -7
- metadata +21 -121
- data/.travis.yml +0 -14
- data/Guardfile +0 -15
data/spec/pause/action_spec.rb
CHANGED
@@ -11,6 +11,10 @@ describe Pause::Action do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
let(:resolution) { 10 }
|
14
|
+
let(:identifier) { '11112222' }
|
15
|
+
let(:action) { MyNotification.new(identifier) }
|
16
|
+
let(:other_identifier) { '8798734' }
|
17
|
+
let(:other_action) { MyNotification.new(other_identifier) }
|
14
18
|
let(:history) { 60 }
|
15
19
|
let(:configuration) { Pause::Configuration.new }
|
16
20
|
let(:adapter) { Pause::Redis::Adapter.new(Pause.config) }
|
@@ -22,15 +26,9 @@ describe Pause::Action do
|
|
22
26
|
allow(Pause).to receive(:adapter).and_return(adapter)
|
23
27
|
end
|
24
28
|
|
25
|
-
let(:identifier) { '11112222' }
|
26
|
-
let(:action) { MyNotification.new(identifier) }
|
27
|
-
|
28
|
-
let(:other_identifier) { '8798734' }
|
29
|
-
let(:other_action) { MyNotification.new(other_identifier) }
|
30
|
-
|
31
29
|
RSpec.shared_examples 'an action' do
|
32
30
|
describe '#increment!' do
|
33
|
-
it '
|
31
|
+
it 'increments' do
|
34
32
|
time = Time.now
|
35
33
|
Timecop.freeze time do
|
36
34
|
expect(Pause.adapter).to receive(:increment).with(action.scope, identifier, time.to_i, 1)
|
@@ -40,7 +38,7 @@ describe Pause::Action do
|
|
40
38
|
end
|
41
39
|
|
42
40
|
describe '#ok?' do
|
43
|
-
it '
|
41
|
+
it 'successfullies return if the action is blocked or not' do
|
44
42
|
time = Time.now
|
45
43
|
Timecop.freeze time do
|
46
44
|
4.times do
|
@@ -52,7 +50,7 @@ describe Pause::Action do
|
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
55
|
-
it '
|
53
|
+
it 'successfullies consider different period checks' do
|
56
54
|
time = Time.parse('Sept 22, 11:34:00')
|
57
55
|
|
58
56
|
Timecop.freeze time - 30 do
|
@@ -71,7 +69,7 @@ describe Pause::Action do
|
|
71
69
|
end
|
72
70
|
end
|
73
71
|
|
74
|
-
it '
|
72
|
+
it 'returns false and silently fail if redis is not available' do
|
75
73
|
allow(Pause::Logger).to receive(:fatal)
|
76
74
|
allow_any_instance_of(Redis).to receive(:zrange).and_raise Redis::CannotConnectError
|
77
75
|
time = period_marker(resolution, Time.now.to_i)
|
@@ -86,7 +84,7 @@ describe Pause::Action do
|
|
86
84
|
context 'action should not be rate limited' do
|
87
85
|
it 'returns nil' do
|
88
86
|
expect(adapter.rate_limited?(action.scope, action.identifier)).to be false
|
89
|
-
expect(action.analyze).to
|
87
|
+
expect(action.analyze).to be_nil
|
90
88
|
end
|
91
89
|
end
|
92
90
|
|
@@ -130,7 +128,7 @@ describe Pause::Action do
|
|
130
128
|
end
|
131
129
|
|
132
130
|
context 'actions under test' do
|
133
|
-
['123456', 'hello', 0,
|
131
|
+
['123456', 'hello', 0, 999_999].each do |id|
|
134
132
|
let(:identifier) { id }
|
135
133
|
let(:action) { MyNotification.new(identifier) }
|
136
134
|
describe "action with identifier #{id}" do
|
@@ -151,38 +149,47 @@ describe Pause::Action do
|
|
151
149
|
let(:bogus) { Struct.new(:name, :event).new }
|
152
150
|
|
153
151
|
describe '#unless_rate_limited' do
|
154
|
-
before
|
155
|
-
|
152
|
+
before { expect(bogus).to receive(:name).twice }
|
153
|
+
|
154
|
+
describe '#initialize' do
|
155
|
+
before { expect(bogus).to receive(:event).once }
|
156
|
+
|
157
|
+
it 'is able to use methods inside the #new block' do
|
158
|
+
b = bogus
|
159
|
+
CowRateLimited.new(identifier) do
|
160
|
+
unless_rate_limited { b.name } # this executes
|
161
|
+
unless_rate_limited { b.name } # this also executes
|
162
|
+
unless_rate_limited { b.name } # and this will be rate limited
|
163
|
+
if_rate_limited { b.event }
|
164
|
+
end
|
165
|
+
end
|
156
166
|
end
|
157
|
-
|
167
|
+
|
168
|
+
it 'calls through the block' do
|
158
169
|
action.unless_rate_limited { bogus.name }
|
159
170
|
action.unless_rate_limited { bogus.name }
|
160
171
|
result = action.unless_rate_limited { bogus.name }
|
161
|
-
expect(result).to
|
172
|
+
expect(result).to be_a(Pause::RateLimitedEvent)
|
162
173
|
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe '#unless_rate_limited' do
|
166
|
-
before { expect(bogus).to receive(:name).exactly(2).times }
|
167
174
|
|
168
|
-
it '
|
175
|
+
it 'calls through the block' do
|
169
176
|
3.times { action.unless_rate_limited { bogus.name } }
|
170
177
|
end
|
171
178
|
|
172
179
|
describe '#if_rate_limited' do
|
173
180
|
before { 2.times { action.unless_rate_limited { bogus.name } } }
|
174
181
|
|
175
|
-
it '
|
182
|
+
it 'does not analyze during method call' do
|
176
183
|
bogus.event = 1
|
177
184
|
action.if_rate_limited { |event| bogus.event = event }
|
178
|
-
expect(bogus.event).to
|
185
|
+
expect(bogus.event).to be_a(Pause::RateLimitedEvent)
|
179
186
|
expect(bogus.event.identifier).to eq(identifier)
|
180
187
|
end
|
181
188
|
|
182
|
-
it '
|
189
|
+
it 'analyzes if requested' do
|
183
190
|
action.unless_rate_limited { bogus.name }
|
184
191
|
result = action.if_rate_limited { |event| bogus.event = event }
|
185
|
-
expect(bogus.event).to
|
192
|
+
expect(bogus.event).to be_a(Pause::RateLimitedEvent)
|
186
193
|
expect(result).to eq(bogus.event)
|
187
194
|
end
|
188
195
|
end
|
@@ -190,7 +197,7 @@ describe Pause::Action do
|
|
190
197
|
end
|
191
198
|
|
192
199
|
describe '.tracked_identifiers' do
|
193
|
-
it '
|
200
|
+
it 'returns all the identifiers tracked (but not blocked) so far' do
|
194
201
|
action.increment!
|
195
202
|
other_action.increment!
|
196
203
|
|
@@ -203,7 +210,7 @@ describe Pause::Action do
|
|
203
210
|
end
|
204
211
|
|
205
212
|
describe '.rate_limited_identifiers' do
|
206
|
-
it '
|
213
|
+
it 'returns all the identifiers blocked' do
|
207
214
|
action.increment!(100, Time.now.to_i)
|
208
215
|
other_action.increment!(100, Time.now.to_i)
|
209
216
|
|
@@ -216,7 +223,7 @@ describe Pause::Action do
|
|
216
223
|
end
|
217
224
|
|
218
225
|
describe '.unblock_all' do
|
219
|
-
it '
|
226
|
+
it 'unblocks all the identifiers for a scope' do
|
220
227
|
10.times { action.increment! }
|
221
228
|
other_action.increment!
|
222
229
|
|
@@ -232,7 +239,6 @@ describe Pause::Action do
|
|
232
239
|
expect(MyNotification.tracked_identifiers).to eq([other_action.identifier])
|
233
240
|
end
|
234
241
|
end
|
235
|
-
|
236
242
|
end
|
237
243
|
|
238
244
|
describe Pause::Action, '.check' do
|
@@ -250,22 +256,22 @@ describe Pause::Action, '.check' do
|
|
250
256
|
check period_seconds: 50, block_ttl: 60, max_allowed: 100
|
251
257
|
end
|
252
258
|
|
253
|
-
it '
|
259
|
+
it 'defines a period check on new instances' do
|
254
260
|
expect(ActionWithCheck.new('id').checks).to eq([
|
255
261
|
Pause::PeriodCheck.new(100, 150, 200)
|
256
262
|
])
|
257
263
|
end
|
258
264
|
|
259
|
-
it '
|
265
|
+
it 'defines a period check on new instances' do
|
260
266
|
expect(ActionWithMultipleChecks.new('id').checks).to \
|
261
267
|
eq([
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
268
|
+
Pause::PeriodCheck.new(100, 150, 200),
|
269
|
+
Pause::PeriodCheck.new(200, 150, 200),
|
270
|
+
Pause::PeriodCheck.new(300, 150, 200)
|
271
|
+
])
|
266
272
|
end
|
267
273
|
|
268
|
-
it '
|
274
|
+
it 'accepts hash arguments' do
|
269
275
|
expect(ActionWithHashChecks.new('id').checks).to eq([
|
270
276
|
Pause::PeriodCheck.new(50, 100, 60)
|
271
277
|
])
|
@@ -278,7 +284,7 @@ describe Pause::Action, '.scope' do
|
|
278
284
|
end
|
279
285
|
end
|
280
286
|
|
281
|
-
it '
|
287
|
+
it 'raises if scope is not defined' do
|
282
288
|
expect(MyApp::NoScope.new('1.2.3.4').scope).to eq 'myapp.noscope'
|
283
289
|
end
|
284
290
|
|
@@ -286,7 +292,7 @@ describe Pause::Action, '.scope' do
|
|
286
292
|
scope 'my:scope'
|
287
293
|
end
|
288
294
|
|
289
|
-
it '
|
295
|
+
it 'sets scope on class' do
|
290
296
|
expect(DefinedScopeAction.new('1.2.3.4').scope).to eq('my:scope')
|
291
297
|
end
|
292
298
|
end
|
@@ -309,26 +315,26 @@ describe Pause::Action, 'enabled/disabled states' do
|
|
309
315
|
describe '#disable' do
|
310
316
|
before do
|
311
317
|
expect(action).to be_enabled
|
312
|
-
expect(action).
|
318
|
+
expect(action).not_to be_disabled
|
313
319
|
action.disable
|
314
320
|
end
|
315
321
|
|
316
322
|
it 'disables the action' do
|
317
323
|
expect(action).to be_disabled
|
318
|
-
expect(action).
|
324
|
+
expect(action).not_to be_enabled
|
319
325
|
end
|
320
326
|
end
|
321
327
|
|
322
328
|
describe '#enable' do
|
323
329
|
before do
|
324
330
|
action.disable
|
325
|
-
expect(action).
|
331
|
+
expect(action).not_to be_enabled
|
326
332
|
action.enable
|
327
333
|
end
|
328
334
|
|
329
335
|
it 'enables the action' do
|
330
336
|
expect(action).to be_enabled
|
331
|
-
expect(action).
|
337
|
+
expect(action).not_to be_disabled
|
332
338
|
end
|
333
339
|
end
|
334
340
|
end
|
data/spec/pause/analyzer_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'timecop'
|
3
5
|
|
@@ -11,20 +13,17 @@ describe Pause::Analyzer do
|
|
11
13
|
end
|
12
14
|
|
13
15
|
let(:resolution) { 10 }
|
16
|
+
let(:analyzer) { Pause.analyzer }
|
17
|
+
let(:action) { FollowPushNotification.new('1243123') }
|
14
18
|
let(:history) { 60 }
|
15
19
|
let(:configuration) { Pause::Configuration.new }
|
16
20
|
let(:adapter) { Pause::Redis::Adapter.new(configuration) }
|
17
21
|
|
18
22
|
before do
|
19
|
-
allow(Pause).to
|
20
|
-
allow(Pause
|
21
|
-
allow(Pause.config).to receive(:history).and_return(history)
|
22
|
-
allow(Pause).to receive(:adapter).and_return(adapter)
|
23
|
+
allow(Pause.config).to receive_messages(resolution: resolution, history: history)
|
24
|
+
allow(Pause).to receive_messages(config: configuration, adapter: adapter)
|
23
25
|
end
|
24
26
|
|
25
|
-
let(:analyzer) { Pause.analyzer }
|
26
|
-
let(:action) { FollowPushNotification.new('1243123') }
|
27
|
-
|
28
27
|
describe '#analyze' do
|
29
28
|
it 'checks and blocks if max_allowed is reached' do
|
30
29
|
time = Time.now
|
@@ -39,11 +38,15 @@ describe Pause::Analyzer do
|
|
39
38
|
end
|
40
39
|
|
41
40
|
describe '#check' do
|
42
|
-
it '
|
43
|
-
expect(analyzer.check(action)).to
|
41
|
+
it 'returns nil if action is NOT blocked' do
|
42
|
+
expect(analyzer.check(action)).to be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns nil if action is NOT rate limited' do
|
46
|
+
expect(action).not_to be_rate_limited
|
44
47
|
end
|
45
48
|
|
46
|
-
it '
|
49
|
+
it 'returns blocked action if action is blocked' do
|
47
50
|
Timecop.freeze Time.now do
|
48
51
|
5.times do
|
49
52
|
action.increment!
|
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Pause::Configuration, '#configure' do
|
6
|
+
subject { described_class.new }
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
it 'should allow configuration via block' do
|
8
|
+
it 'allows configuration via block' do
|
8
9
|
subject.configure do |c|
|
9
10
|
c.redis_host = '128.23.12.8'
|
10
11
|
c.redis_port = '2134'
|
@@ -24,7 +25,7 @@ describe Pause::Configuration, '#configure' do
|
|
24
25
|
expect(subject.sharded).to be true
|
25
26
|
end
|
26
27
|
|
27
|
-
it '
|
28
|
+
it 'provides redis defaults' do
|
28
29
|
subject.configure do |config|
|
29
30
|
# do nothing
|
30
31
|
end
|
@@ -33,7 +34,7 @@ describe Pause::Configuration, '#configure' do
|
|
33
34
|
expect(subject.redis_port).to eq(6379)
|
34
35
|
expect(subject.redis_db).to eq('1')
|
35
36
|
expect(subject.resolution).to eq(600) # 10 minutes
|
36
|
-
expect(subject.history).to eq(
|
37
|
+
expect(subject.history).to eq(86_400) # one day
|
37
38
|
expect(subject.sharded).to be false
|
38
39
|
end
|
39
40
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Pause::Logger do
|
6
|
+
describe 'when accessed #puts' do
|
7
|
+
before do
|
8
|
+
expect($stdout).to receive(:puts).with('hello')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'calls through to puts without color' do
|
12
|
+
described_class.puts('hello')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'when accessed via #fatal' do
|
17
|
+
before do
|
18
|
+
expect($stderr).to receive(:puts).with("\e[31mwhoops\e[0m")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'calls through to puts with color' do
|
22
|
+
described_class.fatal('whoops')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/pause/pause_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
RSpec.describe Pause do
|
@@ -5,8 +7,8 @@ RSpec.describe Pause do
|
|
5
7
|
let(:configuration) { Pause::Configuration.new }
|
6
8
|
|
7
9
|
before do
|
8
|
-
|
9
|
-
allow(
|
10
|
+
described_class.adapter = nil
|
11
|
+
allow(described_class).to receive(:config).and_return(configuration)
|
10
12
|
configuration.configure { |c| c.sharded = sharded }
|
11
13
|
end
|
12
14
|
|
@@ -14,7 +16,7 @@ RSpec.describe Pause do
|
|
14
16
|
let(:sharded) { true }
|
15
17
|
|
16
18
|
it 'is a ShardedAdapter' do
|
17
|
-
expect(
|
19
|
+
expect(described_class.adapter).to be_a(Pause::Redis::ShardedAdapter)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -22,7 +24,7 @@ RSpec.describe Pause do
|
|
22
24
|
let(:sharded) { false }
|
23
25
|
|
24
26
|
it 'is an Adapter' do
|
25
|
-
expect(
|
27
|
+
expect(described_class.adapter).to be_a(Pause::Redis::Adapter)
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
module Pause
|
6
|
+
RSpec.describe PeriodCheck do
|
7
|
+
let(:period_seconds) { 10 }
|
8
|
+
let(:max_allowed) { 2 }
|
9
|
+
let(:block_ttl) { 10 }
|
10
|
+
|
11
|
+
let(:period_check_1) do
|
12
|
+
described_class.new(
|
13
|
+
period_seconds: period_seconds,
|
14
|
+
max_allowed: max_allowed,
|
15
|
+
block_ttl: block_ttl
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:period_check_2) do
|
20
|
+
described_class.new(
|
21
|
+
period_seconds: 2 * period_seconds,
|
22
|
+
max_allowed: 1.5 * max_allowed,
|
23
|
+
block_ttl:
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#==' do
|
28
|
+
let(:period_check_3) do
|
29
|
+
described_class.new(
|
30
|
+
period_seconds: 2 * period_seconds,
|
31
|
+
max_allowed: 1.5 * max_allowed,
|
32
|
+
block_ttl:
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is equal' do
|
37
|
+
expect(period_check_3).to eq(period_check_2)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is not equal' do
|
41
|
+
expect(period_check_3).not_to eq(period_check_1)
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#sort' do
|
45
|
+
let(:list) { [period_check_2, period_check_1] }
|
46
|
+
|
47
|
+
it 'sorts' do
|
48
|
+
expect(list.sort).to eq([period_check_1, period_check_2])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,59 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'date'
|
3
5
|
require 'timecop'
|
4
6
|
|
5
7
|
describe Pause::Redis::Adapter do
|
6
|
-
|
7
8
|
let(:resolution) { 10 }
|
9
|
+
let(:adapter) { described_class.new(Pause.config) }
|
10
|
+
let(:redis_conn) { adapter.send(:redis) }
|
8
11
|
let(:history) { 60 }
|
9
12
|
let(:configuration) { Pause::Configuration.new }
|
10
13
|
|
11
14
|
before do
|
12
15
|
allow(Pause).to receive(:config).and_return(configuration)
|
13
|
-
allow(Pause.config).to
|
14
|
-
allow(Pause.config).to receive(:history).and_return(history)
|
16
|
+
allow(Pause.config).to receive_messages(resolution: resolution, history: history)
|
15
17
|
redis_conn.flushall
|
16
18
|
end
|
17
19
|
|
18
|
-
let(:adapter) { Pause::Redis::Adapter.new(Pause.config) }
|
19
|
-
let(:redis_conn) { adapter.send(:redis) }
|
20
|
-
|
21
20
|
describe '#increment' do
|
22
21
|
let(:scope) { 'blah' }
|
23
22
|
let(:identifier) { '213213' }
|
24
|
-
let(:tracked_key) {
|
23
|
+
let(:tracked_key) { "i:#{scope}:|#{identifier}|" }
|
24
|
+
let(:now) { Time.now.to_i }
|
25
25
|
|
26
|
-
it '
|
26
|
+
it 'adds key to a redis set' do
|
27
27
|
adapter.increment(scope, identifier, Time.now.to_i)
|
28
|
-
set = redis_conn.zrange(tracked_key, 0, -1, :
|
29
|
-
expect(set).
|
30
|
-
expect(set.size).to
|
31
|
-
expect(set[0].size).to
|
28
|
+
set = redis_conn.zrange(tracked_key, 0, -1, with_scores: true)
|
29
|
+
expect(set).not_to be_empty
|
30
|
+
expect(set.size).to be(1)
|
31
|
+
expect(set[0].size).to be(2)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'when increment is called' do
|
35
|
+
let(:redis_conn_double) { instance_double(Redis) }
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(adapter).to receive(:with_multi).and_yield(redis_conn_double)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'calls zincrby on the redis connection' do
|
42
|
+
allow(redis_conn_double).to receive(:expire)
|
43
|
+
expect(redis_conn_double).to receive(:zincrby)
|
44
|
+
|
45
|
+
adapter.increment(scope, identifier, Time.now.to_i)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'calls expire on the redis key' do
|
49
|
+
expect(redis_conn_double).to receive(:expire).with(tracked_key, history)
|
50
|
+
allow(redis_conn_double).to receive(:zincrby)
|
51
|
+
|
52
|
+
adapter.increment(scope, identifier, Time.now.to_i)
|
53
|
+
end
|
32
54
|
end
|
33
55
|
|
34
56
|
context 'removing two elements' do
|
35
57
|
let(:to_delete) { 2 }
|
36
|
-
let(:
|
58
|
+
let(:period_start) { adapter.period_marker(resolution, now) }
|
59
|
+
let(:period_end) { adapter.period_marker(resolution, now + resolution) }
|
60
|
+
|
61
|
+
around do |example|
|
62
|
+
Timecop.freeze(now) { example.run }
|
63
|
+
end
|
64
|
+
|
37
65
|
before do
|
38
|
-
|
39
|
-
to_delete.times do |t|
|
40
|
-
expect(redis_conn).to receive(:zrem).with(tracked_key, [adapter.period_marker(resolution, time + t)]).once
|
41
|
-
end
|
66
|
+
redis_conn.flushall
|
42
67
|
adapter.time_blocks_to_keep = 1
|
68
|
+
allow(redis_conn).to receive(:zrem).with(tracked_key, [period_start])
|
69
|
+
allow(redis_conn).to receive(:zrem).with(tracked_key, [period_start, period_end])
|
43
70
|
end
|
44
|
-
|
45
|
-
|
71
|
+
|
72
|
+
it 'removes old elements' do
|
73
|
+
adapter.increment(scope, identifier, now.to_i)
|
46
74
|
to_delete.times do |t|
|
47
|
-
next_time =
|
75
|
+
next_time = now + (adapter.resolution + t + 1)
|
48
76
|
adapter.increment(scope, identifier, next_time.to_i)
|
49
77
|
end
|
50
78
|
end
|
51
79
|
end
|
52
|
-
|
53
|
-
it 'sets expiry on key' do
|
54
|
-
expect(redis_conn).to receive(:expire).with(tracked_key, history)
|
55
|
-
adapter.increment(scope, identifier, Time.now.to_i)
|
56
|
-
end
|
57
80
|
end
|
58
81
|
|
59
82
|
describe '#expire_block_list' do
|
@@ -74,21 +97,18 @@ describe Pause::Redis::Adapter do
|
|
74
97
|
|
75
98
|
adapter.expire_block_list(scope)
|
76
99
|
|
77
|
-
expect(redis_conn.zscore('b:|a|', blocked_identifier)).not_to
|
78
|
-
expect(redis_conn.zscore('b:|a|', expired_identifier)).to
|
100
|
+
expect(redis_conn.zscore('b:|a|', blocked_identifier)).not_to be_nil
|
101
|
+
expect(redis_conn.zscore('b:|a|', expired_identifier)).to be_nil
|
79
102
|
end
|
80
103
|
end
|
81
104
|
|
82
|
-
describe '#rate_limit!' do
|
83
|
-
end
|
84
|
-
|
85
105
|
describe '#rate_limited?' do
|
86
106
|
let(:scope) { 'ipn:follow' }
|
87
107
|
let(:identifier) { '123461234' }
|
88
108
|
let(:blocked_key) { "b:#{key}" }
|
89
|
-
let(:ttl) {
|
109
|
+
let(:ttl) { 110_000 }
|
90
110
|
|
91
|
-
it '
|
111
|
+
it 'returns true if blocked' do
|
92
112
|
adapter.rate_limit!(scope, identifier, ttl)
|
93
113
|
expect(adapter.rate_limited?(scope, identifier)).to be true
|
94
114
|
end
|
@@ -128,17 +148,16 @@ describe Pause::Redis::Adapter do
|
|
128
148
|
let(:scope) { 'ipn:follow' }
|
129
149
|
let(:identifier) { '1234' }
|
130
150
|
let(:blocked_key) { "b:|#{scope}|" }
|
131
|
-
let(:ttl) {
|
151
|
+
let(:ttl) { 110_000 }
|
132
152
|
|
133
153
|
it 'saves ip to redis with expiration' do
|
134
154
|
time = Time.now
|
135
155
|
Timecop.freeze time do
|
136
156
|
adapter.rate_limit!(scope, identifier, ttl)
|
137
157
|
end
|
138
|
-
expect(redis_conn.zscore(blocked_key, identifier)).
|
158
|
+
expect(redis_conn.zscore(blocked_key, identifier)).not_to be_nil
|
139
159
|
expect(redis_conn.zscore(blocked_key, identifier)).to eq(time.to_i + ttl)
|
140
160
|
end
|
141
|
-
|
142
161
|
end
|
143
162
|
end
|
144
163
|
|
@@ -1,21 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'date'
|
3
5
|
require 'timecop'
|
4
6
|
|
5
7
|
describe Pause::Redis::ShardedAdapter do
|
6
|
-
|
7
8
|
let(:resolution) { 10 }
|
9
|
+
let(:adapter) { described_class.new(Pause.config) }
|
8
10
|
let(:history) { 60 }
|
9
11
|
let(:configuration) { Pause::Configuration.new }
|
10
12
|
|
11
13
|
before do
|
12
14
|
allow(Pause).to receive(:config).and_return(configuration)
|
13
|
-
allow(Pause.config).to
|
14
|
-
allow(Pause.config).to receive(:history).and_return(history)
|
15
|
+
allow(Pause.config).to receive_messages(resolution: resolution, history: history)
|
15
16
|
end
|
16
17
|
|
17
|
-
let(:adapter) { Pause::Redis::ShardedAdapter.new(Pause.config) }
|
18
|
-
|
19
18
|
describe '#all_keys' do
|
20
19
|
it 'is not supported' do
|
21
20
|
expect { adapter.all_keys('cake') }.to raise_error(Pause::Redis::OperationNotSupported)
|
@@ -24,15 +23,16 @@ describe Pause::Redis::ShardedAdapter do
|
|
24
23
|
|
25
24
|
describe '#with_multi' do
|
26
25
|
let(:redis) { adapter.send(:redis) }
|
27
|
-
|
28
|
-
|
29
|
-
expect
|
26
|
+
|
27
|
+
it 'does not call redis.multi' do
|
28
|
+
expect(redis).not_to receive(:multi)
|
29
|
+
expect { adapter.increment(:scope, 123, Time.now) }.not_to raise_error
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe '#redis' do
|
34
|
-
it '
|
35
|
-
expect(adapter.send(:redis_connection_opts)).
|
34
|
+
it 'does not use redis db when connecting' do
|
35
|
+
expect(adapter.send(:redis_connection_opts)).not_to include(:db)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
4
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
5
|
# Require this file using `require "spec_helper"` to ensure that it is only
|
@@ -5,28 +7,30 @@
|
|
5
7
|
#
|
6
8
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
9
|
|
8
|
-
require 'pause'
|
9
10
|
require 'fileutils'
|
10
11
|
|
12
|
+
require 'simplecov'
|
13
|
+
SimpleCov.start
|
14
|
+
|
15
|
+
require 'pause'
|
16
|
+
|
11
17
|
if ENV['PAUSE_REAL_REDIS']
|
12
18
|
require 'pause/redis/adapter'
|
13
|
-
puts
|
19
|
+
puts
|
20
|
+
puts "NOTE: Using real Redis-server at #{Pause::Redis::Adapter.redis.inspect}\n\n"
|
14
21
|
else
|
15
22
|
require 'fakeredis/rspec'
|
16
23
|
end
|
17
24
|
|
18
25
|
RSpec.configure do |config|
|
19
|
-
|
20
|
-
config.filter_run :focus
|
21
|
-
|
22
|
-
rspec_dir = './.spec'.freeze
|
26
|
+
rspec_dir = './.spec'
|
23
27
|
FileUtils.mkdir_p(rspec_dir)
|
24
28
|
config.example_status_persistence_file_path = "#{rspec_dir}/results.txt"
|
25
29
|
|
26
30
|
config.order = 'random'
|
27
31
|
|
28
32
|
if ENV['PAUSE_REAL_REDIS']
|
29
|
-
config.before
|
33
|
+
config.before do
|
30
34
|
Pause::Redis::Adapter.redis.flushdb
|
31
35
|
end
|
32
36
|
end
|