Skip to content

Commit 18e637a

Browse files
committed
Allow to reset state between tests
1 parent c962dc1 commit 18e637a

4 files changed

Lines changed: 51 additions & 0 deletions

File tree

lib/rack/attack.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Attack
1414
class Error < StandardError; end
1515
class MisconfiguredStoreError < Error; end
1616
class MissingStoreError < Error; end
17+
class IncompatibleStoreError < Error; end
1718

1819
autoload :Check, 'rack/attack/check'
1920
autoload :Throttle, 'rack/attack/throttle'
@@ -53,6 +54,10 @@ def clear!
5354
@configuration.clear_configuration
5455
end
5556

57+
def reset!
58+
cache.reset!
59+
end
60+
5661
extend Forwardable
5762
def_delegators(
5863
:@configuration,

lib/rack/attack/cache.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ def delete(unprefixed_key)
4141
store.delete("#{prefix}:#{unprefixed_key}")
4242
end
4343

44+
def reset!
45+
if store.respond_to?(:delete_matched)
46+
store.delete_matched("#{prefix}*")
47+
else
48+
raise(
49+
Rack::Attack::IncompatibleStoreError,
50+
"Configured store #{store.class.name} doesn't respond to #delete_matched method"
51+
)
52+
end
53+
end
54+
4455
private
4556

4657
def key_and_expiry(unprefixed_key, period)

lib/rack/attack/store_proxy/redis_proxy.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ def delete(key, _options = {})
4343
rescuing { del(key) }
4444
end
4545

46+
def delete_matched(matcher, _options = nil)
47+
cursor = "0"
48+
49+
rescuing do
50+
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
51+
loop do
52+
cursor, keys = scan(cursor, match: matcher, count: 1000)
53+
del(*keys) unless keys.empty?
54+
break if cursor == "0"
55+
end
56+
end
57+
end
58+
4659
private
4760

4861
def rescuing

spec/rack_attack_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,26 @@
9999
end
100100
end
101101
end
102+
103+
describe 'reset!' do
104+
it 'raises an error when is not supported by cache store' do
105+
Rack::Attack.cache.store = Class.new
106+
assert_raises(Rack::Attack::IncompatibleStoreError) do
107+
Rack::Attack.reset!
108+
end
109+
end
110+
111+
if defined?(Redis)
112+
it 'should delete rack attack keys' do
113+
redis = Redis.new
114+
redis.set('key', 'value')
115+
redis.set("#{Rack::Attack.cache.prefix}::key", 'value')
116+
Rack::Attack.cache.store = redis
117+
Rack::Attack.reset!
118+
119+
_(redis.get('key')).must_equal 'value'
120+
_(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
121+
end
122+
end
123+
end
102124
end

0 commit comments

Comments
 (0)