require 'test_helper'

module Workarea
  class RackAttackIntegrationTest < Workarea::IntegrationTest
    class AddEnvMiddleware
      def initialize(app)
        @app = app
      end

      def call(env)
        env.merge!(Rails.application.env_config)
        @app.call(env)
      end
    end

    def test_safelist_kubernetes_health_checks
      get '/', env: { 'REMOTE_ADDR' => Rack::Attack::KUBERNETES_IP_ADDRESS }

      assert_response(:success)
      assert_equal('ignore/k8s', request.env['rack.attack.matched'])
      assert_equal(:safelist, request.env['rack.attack.match_type'])
    end

    def test_throttle_all_requests
      ip = '192.168.1.1'

      get '/', env: { 'REMOTE_ADDR' => ip }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("req/ip:#{ip}", 300).returns(301)

      get '/', env: { 'REMOTE_ADDR' => ip }

      assert_response(429)
      assert_equal('req/ip', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_login_attempts_by_ip
      ip = '192.168.1.1'

      post '/login', env: { 'REMOTE_ADDR' => ip }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("logins/ip:#{ip}", 20).returns(6)

      post '/login', env: { 'REMOTE_ADDR' => ip }

      assert_response(429)
      assert_equal('logins/ip', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_login_attempts_by_email
      email = 'test@example.com'

      post '/login', params: { email: email }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("logins/email:#{email}", 20).returns(6)

      post '/login', params: { email: email }

      assert_response(429)
      assert_equal('logins/email', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_contact_requests_by_ip
      ip = '192.168.1.1'

      post '/contact', env: { 'REMOTE_ADDR' => ip }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("contact/ip:#{ip}", 1.minute.to_i).returns(4)
      post '/contact', env: { 'REMOTE_ADDR' => ip }

      assert_response(429)
      assert_equal('contact/ip', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_email_signups_by_ip
      ip = '192.168.1.1'

      post '/email_signup', env: { 'REMOTE_ADDR' => ip }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("email_signup/ip:#{ip}", 20.minutes.to_i).returns(11)
      post '/email_signup', env: { 'REMOTE_ADDR' => ip }

      assert_response(429)
      assert_equal('email_signup/ip', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_email_signups_by_email
      email = 'test@example.com'

      post '/email_signup', params: { email: email }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("email_signup/email:#{email}", 20.minutes.to_i).returns(11)
      post '/email_signup', params: { email: email }

      assert_response(429)
      assert_equal('email_signup/email', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_password_resets_by_email
      email = 'test@example.com'

      post '/forgot_password', params: { email: email }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("password_reset/email:#{email}", 20.minutes.to_i).returns(11)
      post '/forgot_password', params: { email: email }

      assert_response(429)
      assert_equal('password_reset/email', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    def test_throttle_password_resets_by_ip
      ip = '192.168.1.1'

      post '/forgot_password', env: { 'REMOTE_ADDR' => ip }

      assert_response(:success)

      Rack::Attack.cache.stubs(:count).returns(0)
      Rack::Attack.cache.stubs(:count).with("password_reset/ip:#{ip}", 20.minutes.to_i).returns(11)
      post '/forgot_password', env: { 'REMOTE_ADDR' => ip }

      assert_response(429)
      assert_equal('password_reset/ip', request.env['rack.attack.matched'])
      assert_equal(:throttle, request.env['rack.attack.match_type'])
    end

    private

    def app
      @app ||= begin
        Rack::Builder.new do
          use AddEnvMiddleware
          use Rack::Attack
          use ActionDispatch::Cookies
          run -> (*) { [ 200, {}, [] ] }
        end
      end
    end
  end
end