From cabadf3dc0cd3747e86074f5bc4776a1a2402203 Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Sat, 15 Mar 2014 14:21:37 -0400 Subject: [PATCH 1/5] Better organize integration tests Add rake tasks `test:units` and `test:integration` Run integration tests by default on TravisCi. Run memcached and redis on TravisCi. --- .travis.yml | 4 + Rakefile | 13 ++- spec/integration/rack_attack_cache_spec.rb | 108 ++++++++++++++++++++ spec/rack_attack_cache_spec.rb | 112 --------------------- 4 files changed, 123 insertions(+), 114 deletions(-) create mode 100644 spec/integration/rack_attack_cache_spec.rb delete mode 100644 spec/rack_attack_cache_spec.rb diff --git a/.travis.yml b/.travis.yml index 5456a02..c55040c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,3 +11,7 @@ rvm: gemfile: - gemfiles/activesupport3.2 - gemfiles/activesupport4.0 + +services: + - redis + - memcached diff --git a/Rakefile b/Rakefile index 3c19db8..0475689 100644 --- a/Rakefile +++ b/Rakefile @@ -2,8 +2,17 @@ require "rubygems" require "bundler/setup" require 'rake/testtask' -Rake::TestTask.new do |t| - t.pattern = "spec/*_spec.rb" +namespace :test do + Rake::TestTask.new(:units) do |t| + t.pattern = "spec/*_spec.rb" + end + + Rake::TestTask.new(:integration) do |t| + t.pattern = "spec/integration/*_spec.rb" + end end +desc 'Run tests' +task :test => %w[test:units test:integration] + task :default => :test diff --git a/spec/integration/rack_attack_cache_spec.rb b/spec/integration/rack_attack_cache_spec.rb new file mode 100644 index 0000000..d2f3721 --- /dev/null +++ b/spec/integration/rack_attack_cache_spec.rb @@ -0,0 +1,108 @@ +require_relative '../spec_helper' + +describe Rack::Attack::Cache do + def delete(key) + if @cache.store.respond_to?(:delete) + @cache.store.delete(key) + else + @cache.store.del(key) + end + end + + require 'active_support/cache/dalli_store' + require 'active_support/cache/redis_store' + cache_stores = [ + ActiveSupport::Cache::MemoryStore.new, + ActiveSupport::Cache::DalliStore.new("localhost"), + ActiveSupport::Cache::RedisStore.new("localhost"), + Redis::Store.new + ] + + cache_stores.each do |store| + store = Rack::Attack::StoreProxy.build(store) + describe "with #{store.class}" do + + before { + @cache ||= Rack::Attack::Cache.new + @key = "rack::attack:cache-test-key" + @expires_in = 1 + @cache.store = store + delete(@key) + } + + after { delete(@key) } + + describe "do_count once" do + it "should be 1" do + @cache.send(:do_count, @key, @expires_in).must_equal 1 + end + end + + describe "do_count twice" do + it "must be 2" do + @cache.send(:do_count, @key, @expires_in) + @cache.send(:do_count, @key, @expires_in).must_equal 2 + end + end + describe "do_count after expires_in" do + it "must be 1" do + @cache.send(:do_count, @key, @expires_in) + sleep @expires_in # sigh + @cache.send(:do_count, @key, @expires_in).must_equal 1 + end + end + + describe "write" do + it "should write a value to the store with prefix" do + @cache.write("cache-test-key", "foobar", 1) + store.read(@key).must_equal "foobar" + end + end + + describe "write after expiry" do + it "must not have a value" do + @cache.write("cache-test-key", "foobar", @expires_in) + sleep @expires_in # tick... tick... tick... + store.read(@key).must_be :nil? + end + end + + describe "read" do + it "must read the value with a prefix" do + store.write(@key, "foobar", :expires_in => @expires_in) + @cache.read("cache-test-key").must_equal "foobar" + end + end + end + + end + + describe "should not error if redis is not running" do + before { + @cache = Rack::Attack::Cache.new + @key = "rack::attack:cache-test-key" + @expires_in = 1 + # Use ip reserved for documentation to ensure it does not exist + # http://tools.ietf.org/html/rfc5737 + @cache.store = ActiveSupport::Cache::RedisStore.new(:host => '203.0.113.0', :port => 3333) + } + describe "write" do + it "should not raise exception" do + @cache.write("cache-test-key", "foobar", 1) + end + end + + describe "read" do + it "should not raise exception" do + @cache.read("cache-test-key") + end + end + + describe "do_count" do + it "should not raise exception" do + @cache.send(:do_count, @key, @expires_in) + end + end + end + +end diff --git a/spec/rack_attack_cache_spec.rb b/spec/rack_attack_cache_spec.rb deleted file mode 100644 index 0016ff9..0000000 --- a/spec/rack_attack_cache_spec.rb +++ /dev/null @@ -1,112 +0,0 @@ -require_relative 'spec_helper' - -if ENV['TEST_INTEGRATION'] - describe Rack::Attack::Cache do - def delete(key) - if @cache.store.respond_to?(:delete) - @cache.store.delete(key) - else - @cache.store.del(key) - end - end - - require 'active_support/cache/dalli_store' - require 'active_support/cache/redis_store' - cache_stores = [ - ActiveSupport::Cache::MemoryStore.new, - ActiveSupport::Cache::DalliStore.new("localhost"), - ActiveSupport::Cache::RedisStore.new("localhost"), - Redis::Store.new - ] - - cache_stores.each do |store| - store = Rack::Attack::StoreProxy.build(store) - describe "with #{store.class}" do - - before { - @cache ||= Rack::Attack::Cache.new - @key = "rack::attack:cache-test-key" - @expires_in = 1 - @cache.store = store - delete(@key) - } - - after { delete(@key) } - - describe "do_count once" do - it "should be 1" do - @cache.send(:do_count, @key, @expires_in).must_equal 1 - end - end - - describe "do_count twice" do - it "must be 2" do - @cache.send(:do_count, @key, @expires_in) - @cache.send(:do_count, @key, @expires_in).must_equal 2 - end - end - describe "do_count after expires_in" do - it "must be 1" do - @cache.send(:do_count, @key, @expires_in) - sleep @expires_in # sigh - @cache.send(:do_count, @key, @expires_in).must_equal 1 - end - end - - describe "write" do - it "should write a value to the store with prefix" do - @cache.write("cache-test-key", "foobar", 1) - store.read(@key).must_equal "foobar" - end - end - - describe "write after expiry" do - it "must not have a value" do - @cache.write("cache-test-key", "foobar", @expires_in) - sleep @expires_in # tick... tick... tick... - store.read(@key).must_be :nil? - end - end - - describe "read" do - it "must read the value with a prefix" do - store.write(@key, "foobar", :expires_in => @expires_in) - @cache.read("cache-test-key").must_equal "foobar" - end - end - end - - end - - describe "should not error if redis is not running" do - before { - @cache = Rack::Attack::Cache.new - @key = "rack::attack:cache-test-key" - @expires_in = 1 - # Use ip reserved for documentation to ensure it does not exist - # http://tools.ietf.org/html/rfc5737 - @cache.store = ActiveSupport::Cache::RedisStore.new(:host => '203.0.113.0', :port => 3333) - } - describe "write" do - it "should not raise exception" do - @cache.write("cache-test-key", "foobar", 1) - end - end - - describe "read" do - it "should not raise exception" do - @cache.read("cache-test-key") - end - end - - describe "do_count" do - it "should not raise exception" do - @cache.send(:do_count, @key, @expires_in) - end - end - end - - end -else - puts 'Skipping cache store integration tests (set ENV["TEST_INTEGRATION"] to enable)' -end From 2a7ae7d84dad34bd5eaaaa257bfa5532cb8896d9 Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Sat, 15 Mar 2014 14:41:50 -0400 Subject: [PATCH 2/5] Integration tests: less flakiness by sleeping more --- spec/integration/rack_attack_cache_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/integration/rack_attack_cache_spec.rb b/spec/integration/rack_attack_cache_spec.rb index d2f3721..d26d70c 100644 --- a/spec/integration/rack_attack_cache_spec.rb +++ b/spec/integration/rack_attack_cache_spec.rb @@ -9,6 +9,10 @@ describe Rack::Attack::Cache do end end + def sleep_until_expired + sleep(@expires_in * 1.1) # Add 10% to reduce errors + end + require 'active_support/cache/dalli_store' require 'active_support/cache/redis_store' cache_stores = [ @@ -47,7 +51,7 @@ describe Rack::Attack::Cache do describe "do_count after expires_in" do it "must be 1" do @cache.send(:do_count, @key, @expires_in) - sleep @expires_in # sigh + sleep_until_expired @cache.send(:do_count, @key, @expires_in).must_equal 1 end end @@ -62,7 +66,7 @@ describe Rack::Attack::Cache do describe "write after expiry" do it "must not have a value" do @cache.write("cache-test-key", "foobar", @expires_in) - sleep @expires_in # tick... tick... tick... + sleep_until_expired store.read(@key).must_be :nil? end end From 671f3d4c4016034775734c5cfa4074d3e6a60e3c Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Sat, 15 Mar 2014 14:51:15 -0400 Subject: [PATCH 3/5] [travisci] Fix Errno::ENETUNREACH errors in redis integration tests --- spec/integration/rack_attack_cache_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/integration/rack_attack_cache_spec.rb b/spec/integration/rack_attack_cache_spec.rb index d26d70c..06079ce 100644 --- a/spec/integration/rack_attack_cache_spec.rb +++ b/spec/integration/rack_attack_cache_spec.rb @@ -86,9 +86,8 @@ describe Rack::Attack::Cache do @cache = Rack::Attack::Cache.new @key = "rack::attack:cache-test-key" @expires_in = 1 - # Use ip reserved for documentation to ensure it does not exist - # http://tools.ietf.org/html/rfc5737 - @cache.store = ActiveSupport::Cache::RedisStore.new(:host => '203.0.113.0', :port => 3333) + # Use presumably unused port for Redis client + @cache.store = ActiveSupport::Cache::RedisStore.new(:host => '127.0.0.1', :port => 3333) } describe "write" do it "should not raise exception" do From 1767c134ffa6e747056a1624a3e38e9aef6d2ac9 Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Sat, 15 Mar 2014 14:55:14 -0400 Subject: [PATCH 4/5] [travisci] Add ruby 2.1.1 tests --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c55040c..ccbcafa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ rvm: - 1.9.3 - 2.0.0 - 2.1.0 + - 2.1.1 - jruby-19mode gemfile: From 9049433f0bd3b5a90d372fd95b30fa5eb8593656 Mon Sep 17 00:00:00 2001 From: Aaron Suggs Date: Sat, 15 Mar 2014 15:01:03 -0400 Subject: [PATCH 5/5] Update changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e621610..9e15174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Changlog -## Master +## v3.0.0 - 15 March 2014 * Change default blacklisted response to 403 Forbidden (thanks @carpodaster). * Fail gracefully when Redis store is not available; rescue exeption and don't throttle request. (thanks @wkimeria) + * TravisCI runs integration tests. -## v2.3.0 - 11 October 2013 (master) +## v2.3.0 - 11 October 2013 * Allow throttle `limit` argument to be a proc. (thanks @lunks) * Add Allow2Ban, complement of Fail2Ban. (thanks @jormon) * Improved TravisCI testing