require 'spec_helper'

describe Bugsnag::Rack do
  it "calls the upstream rack app with the environment" do
    rack_env = {"key" => "value"}
    app = lambda { |env| ['response', {}, env] }
    rack_stack = Bugsnag::Rack.new(app)

    response = rack_stack.call(rack_env)

    expect(response).to eq(['response', {}, rack_env])
  end

  context "when an exception is raised in rack middleware" do
    # Build a fake crashing rack app
    exception = BugsnagTestException.new("It crashed")
    rack_env = {"key" => "value"}
    app = lambda { |env| raise exception }
    rack_stack = Bugsnag::Rack.new(app)

    it "re-raises the exception" do
      expect { rack_stack.call(rack_env) }.to raise_error(BugsnagTestException)
    end

    it "delivers an exception if auto_notify is enabled" do
      rack_stack.call(rack_env) rescue nil

      expect(Bugsnag).to have_sent_notification{ |payload, headers|
        exception_class = payload["events"].first["exceptions"].first["errorClass"]
        expect(exception_class).to eq(exception.class.to_s)
      }

    end

    it "applies the correct severity reason" do
      rack_stack.call(rack_env) rescue nil

      expect(Bugsnag).to have_sent_notification{ |payload, headers|
        event = get_event_from_payload(payload)
        expect(event["unhandled"]).to be true
        expect(event["severityReason"]).to eq({
          "type" => "unhandledExceptionMiddleware",
          "attributes" => {
            "framework" => "Rack"
          }
        })
      }
    end

    it "does not deliver an exception if auto_notify is disabled" do
      Bugsnag.configure do |config|
        config.auto_notify = false
      end

      rack_stack.call(rack_env) rescue nil

      expect(Bugsnag).not_to have_sent_notification
    end
  end

  context "when running against the middleware" do
    before do
      unless defined?(::Rack)
        @mocked_rack = true
        class Rack
          class Request
          end
        end
      end
    end

    it "correctly redacts from url and referer any value indicated by meta_data_filters" do
      callback = double
      rack_env = {
        :env => true,
        :HTTP_REFERER => "https://bugsnag.com/about?email=hello@world.com&another_param=thing",
        "rack.session" => {
          :session => true
        }
      }

      rack_request = double
      rack_params = {
        :param => 'test'
      }
      allow(rack_request).to receive_messages(
        :params => rack_params,
        :ip => "rack_ip",
        :request_method => "TEST",
        :path => "/TEST_PATH",
        :scheme => "http",
        :host => "test_host",
        :port => 80,
        :referer => "https://bugsnag.com/about?email=hello@world.com&another_param=thing",
        :fullpath => "/TEST_PATH?email=hello@world.com&another_param=thing"
      )
      expect(::Rack::Request).to receive(:new).with(rack_env).and_return(rack_request)

      # modify rack_env to include redacted referer
      report = double("Bugsnag::Report")
      allow(report).to receive(:request_data).and_return({
        :rack_env => rack_env
      })
      expect(report).to receive(:context=).with("TEST /TEST_PATH")
      expect(report).to receive(:user).and_return({})

      config = double
      allow(config).to receive(:send_environment).and_return(true)
      allow(config).to receive(:meta_data_filters).and_return(['email'])
      allow(report).to receive(:configuration).and_return(config)
      expect(report).to receive(:add_tab).once.with(:request, {
        :url => "http://test_host/TEST_PATH?email=[FILTERED]&another_param=thing",
        :httpMethod => "TEST",
        :params => rack_params,
        :referer => "https://bugsnag.com/about?email=[FILTERED]&another_param=thing",
        :clientIp => "rack_ip",
        :headers => {
          "Referer" => "https://bugsnag.com/about?email=[FILTERED]&another_param=thing"
        }
      })
      # rack_env["HTTP_REFERER"] = "https://bugsnag.com/about?email=[FILTERED]&another_param=thing"
      expect(report).to receive(:add_tab).once.with(:environment, rack_env)
      expect(report).to receive(:add_tab).once.with(:session, {
        :session => true
      })

      expect(callback).to receive(:call).with(report)

      middleware = Bugsnag::Middleware::RackRequest.new(callback)
      middleware.call(report)
    end

    it "correctly extracts data from rack middleware" do
      callback = double
      rack_env = {
        :env => true,
        :HTTP_test_key => "test_key",
        "rack.session" => {
          :session => true
        }
      }

      rack_request = double
      rack_params = {
        :param => 'test'
      }
      allow(rack_request).to receive_messages(
        :params => rack_params,
        :ip => "rack_ip",
        :request_method => "TEST",
        :path => "/TEST_PATH",
        :scheme => "http",
        :host => "test_host",
        :port => 80,
        :referer => "referer",
        :fullpath => "/TEST_PATH"
      )
      expect(Rack::Request).to receive(:new).with(rack_env).and_return(rack_request)

      report = double("Bugsnag::Report")
      allow(report).to receive(:request_data).and_return({
        :rack_env => rack_env
      })
      expect(report).to receive(:context=).with("TEST /TEST_PATH")
      expect(report).to receive(:user).and_return({})

      config = double
      allow(config).to receive(:send_environment).and_return(true)
      allow(config).to receive(:meta_data_filters).and_return(nil)
      allow(report).to receive(:configuration).and_return(config)
      expect(report).to receive(:add_tab).once.with(:environment, rack_env)
      expect(report).to receive(:add_tab).once.with(:request, {
        :url => "http://test_host/TEST_PATH",
        :httpMethod => "TEST",
        :params => rack_params,
        :referer => "referer",
        :clientIp => "rack_ip",
        :headers => {
          "Test-Key" => "test_key"
        }
      })
      expect(report).to receive(:add_tab).once.with(:session, {
        :session => true
      })

      expect(callback).to receive(:call).with(report)

      middleware = Bugsnag::Middleware::RackRequest.new(callback)
      middleware.call(report)
    end

    after do
      Object.send(:remove_const, :Rack) if @mocked_rack
    end

  end

  it "don't mess with middlewares list on each req" do
    stub_const('Rack', nil)
    app = lambda { |env| ['200', {}, ['']] }

    Bugsnag::Rack.new(app)

    expect { 2.times { Bugsnag::Rack.new(app) } }.not_to change {
      Bugsnag.configuration.middleware.instance_variable_get(:@middlewares)
    }
  end
end
