Studiosity/grover

View on GitHub
spec/grover/middleware_spec.rb

Summary

Maintainability
A
0 mins
Test Coverage
F
33%
# frozen_string_literal: true

require 'spec_helper'

describe Grover::Middleware do
  subject(:mock_app) do
    builder = Rack::Builder.new
    builder.use described_class
    builder.run downstream
    builder.to_app
  end

  let(:downstream) do
    lambda do |env|
      # take a reference to the original env so we can test the final state
      @response_env = env
      # take a copy of the env so we can test the downstream (current) state
      @request_env = env.deep_dup
      response_size = 0
      response.each { |part| response_size += part.length }
      [200, headers.merge('Content-Length' => response_size.to_s), response]
    end
  end

  let(:app) { Rack::Lint.new(subject) }
  let(:headers) do
    {
      'Content-Type' => 'text/html',
      'ETag' => 'foo',
      'Cache-Control' => 'max-age=2592000, public'
    }
  end
  let(:response) { ['Grover McGroveryface'] }

  attr_reader :request_env, :response_env

  describe '#call' do
    describe 'response content type' do
      context 'when requesting a PDF' do
        it 'returns PDF content type' do
          get 'http://www.example.org/test.pdf'
          expect(last_response.headers['Content-Type']).to eq 'application/pdf'
          response_size = Grover.new('Grover McGroveryface').to_pdf.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end

        it 'matches PDF case insensitive' do
          get 'http://www.example.org/test.PDF'
          expect(last_response.headers['Content-Type']).to eq 'application/pdf'
          response_size = Grover.new('Grover McGroveryface').to_pdf.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end
      end

      context 'when requesting a PNG' do
        before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

        it 'returns PNG content type' do
          get 'http://www.example.org/test.png'
          expect(last_response.headers['Content-Type']).to eq 'image/png'
          response_size = Grover.new('Grover McGroveryface').to_png.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end

        it 'matches PNG case insensitive' do
          get 'http://www.example.org/test.PNG'
          expect(last_response.headers['Content-Type']).to eq 'image/png'
          response_size = Grover.new('Grover McGroveryface').to_png.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end
      end

      context 'when requesting a JPEG' do
        before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

        it 'returns JPEG content type' do
          get 'http://www.example.org/test.jpeg'
          expect(last_response.headers['Content-Type']).to eq 'image/jpeg'
          response_size = Grover.new('Grover McGroveryface').to_jpeg.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end

        it 'matches JPEG case insensitive' do
          get 'http://www.example.org/test.JPEG'
          expect(last_response.headers['Content-Type']).to eq 'image/jpeg'
          response_size = Grover.new('Grover McGroveryface').to_jpeg.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end

        it 'matches JPG case insensitive' do
          get 'http://www.example.org/test.JPG'
          expect(last_response.headers['Content-Type']).to eq 'image/jpeg'
          response_size = Grover.new('Grover McGroveryface').to_jpeg.bytesize
          expect(last_response.body.bytesize).to eq response_size
          expect(last_response.headers['Content-Length']).to eq response_size.to_s
        end
      end

      context 'when request doesnt have an extension' do
        it 'returns the downstream content type' do
          get 'http://www.example.org/test'
          expect(last_response.headers['Content-Type']).to eq 'text/html'
          expect(last_response.body).to eq 'Grover McGroveryface'
          expect(last_response.headers['Content-Length']).to eq '20'
        end
      end

      context 'when request has a non-PDF/PNG/JPEG extension' do
        it 'returns the downstream content type' do
          get 'http://www.example.org/test.html'
          expect(last_response.headers['Content-Type']).to eq 'text/html'
          expect(last_response.body).to eq 'Grover McGroveryface'
          expect(last_response.headers['Content-Length']).to eq '20'
        end
      end
    end

    describe 'rack environment' do
      context 'when requesting a PDF' do
        it 'removes PDF extension from request PATH_INFO and REQUEST_URI' do
          get 'http://www.example.org/test.pdf'
          # env state sent to downstream middleware
          expect(request_env['PATH_INFO']).to eq '/test'
          expect(request_env['REQUEST_URI']).to eq 'http://www.example.org/test'
          expect(request_env['Rack-Middleware-Grover']).to eq 'true'

          # env state bubbling back up
          expect(response_env['PATH_INFO']).to eq '/test.pdf'
          expect(response_env['REQUEST_URI']).to eq 'http://www.example.org/test.pdf'
          expect(response_env['Rack-Middleware-Grover']).to eq 'true'
        end

        context 'when request has inline parameters' do
          it 'includes the parameters in the request URI' do
            get 'http://www.example.org/test.pdf?def=123&abc=456'
            # env state sent to downstream middleware
            expect(request_env['PATH_INFO']).to eq '/test'
            expect(request_env['REQUEST_URI']).to eq 'http://www.example.org/test?def=123&abc=456'
            expect(request_env['Rack-Middleware-Grover']).to eq 'true'

            # env state bubbling back up
            expect(response_env['PATH_INFO']).to eq '/test.pdf'
            expect(response_env['REQUEST_URI']).to eq 'http://www.example.org/test.pdf?def=123&abc=456'
            expect(response_env['Rack-Middleware-Grover']).to eq 'true'
          end
        end

        context 'when app configuration has PDF middleware disabled' do
          before { allow(Grover.configuration).to receive(:use_pdf_middleware).and_return false }

          it 'doesnt assign path environment variables' do
            get 'http://www.example.org/test.pdf'
            # env state sent to downstream middleware
            expect(request_env['PATH_INFO']).to eq '/test.pdf'
            expect(request_env).not_to have_key 'REQUEST_URI'
            expect(request_env).not_to have_key 'Rack-Middleware-Grover'

            # env state bubbling back up
            expect(response_env['PATH_INFO']).to eq '/test.pdf'
            expect(response_env).not_to have_key 'REQUEST_URI'
            expect(response_env).not_to have_key 'Rack-Middleware-Grover'
          end
        end
      end

      context 'when requesting a PNG' do
        it 'doesnt assign path environment variables' do
          get 'http://www.example.org/test.png'
          # env state sent to downstream middleware
          expect(request_env['PATH_INFO']).to eq '/test.png'
          expect(request_env).not_to have_key 'REQUEST_URI'
          expect(request_env).not_to have_key 'Rack-Middleware-Grover'

          # env state bubbling back up
          expect(response_env['PATH_INFO']).to eq '/test.png'
          expect(response_env).not_to have_key 'REQUEST_URI'
          expect(response_env).not_to have_key 'Rack-Middleware-Grover'
        end

        context 'when app configuration has PNG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

          it 'removes PNG extension from request PATH_INFO and REQUEST_URI' do
            get 'http://www.example.org/test.png'
            # env state sent to downstream middleware
            expect(request_env['PATH_INFO']).to eq '/test'
            expect(request_env['REQUEST_URI']).to eq 'http://www.example.org/test'
            expect(request_env['Rack-Middleware-Grover']).to eq 'true'

            # env state bubbling back up
            expect(response_env['PATH_INFO']).to eq '/test.png'
            expect(response_env['REQUEST_URI']).to eq 'http://www.example.org/test.png'
            expect(response_env['Rack-Middleware-Grover']).to eq 'true'
          end
        end
      end

      context 'when requesting a JPEG' do
        it 'doesnt assign path environment variables for JPEG' do
          get 'http://www.example.org/test.jpeg'
          # env state sent to downstream middleware
          expect(request_env['PATH_INFO']).to eq '/test.jpeg'
          expect(request_env).not_to have_key 'REQUEST_URI'
          expect(request_env).not_to have_key 'Rack-Middleware-Grover'

          # env state bubbling back up
          expect(response_env['PATH_INFO']).to eq '/test.jpeg'
          expect(response_env).not_to have_key 'REQUEST_URI'
          expect(response_env).not_to have_key 'Rack-Middleware-Grover'
        end

        it 'doesnt assign path environment variables for JPG' do
          get 'http://www.example.org/test.jpg'
          # env state sent to downstream middleware
          expect(request_env['PATH_INFO']).to eq '/test.jpg'
          expect(request_env).not_to have_key 'REQUEST_URI'
          expect(request_env).not_to have_key 'Rack-Middleware-Grover'

          # env state bubbling back up
          expect(response_env['PATH_INFO']).to eq '/test.jpg'
          expect(response_env).not_to have_key 'REQUEST_URI'
          expect(response_env).not_to have_key 'Rack-Middleware-Grover'
        end

        context 'when app configuration has JPEG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

          it 'removes JPEG extension from request PATH_INFO and REQUEST_URI' do
            get 'http://www.example.org/test.jpeg'
            # env state sent to downstream middleware
            expect(request_env['PATH_INFO']).to eq '/test'
            expect(request_env['REQUEST_URI']).to eq 'http://www.example.org/test'
            expect(request_env['Rack-Middleware-Grover']).to eq 'true'

            # env state bubbling back up
            expect(response_env['PATH_INFO']).to eq '/test.jpeg'
            expect(response_env['REQUEST_URI']).to eq 'http://www.example.org/test.jpeg'
            expect(response_env['Rack-Middleware-Grover']).to eq 'true'
          end

          it 'removes JPG extension from request PATH_INFO and REQUEST_URI' do
            get 'http://www.example.org/test.jpg'
            # env state sent to downstream middleware
            expect(request_env['PATH_INFO']).to eq '/test'
            expect(request_env['REQUEST_URI']).to eq 'http://www.example.org/test'
            expect(request_env['Rack-Middleware-Grover']).to eq 'true'

            # env state bubbling back up
            expect(response_env['PATH_INFO']).to eq '/test.jpg'
            expect(response_env['REQUEST_URI']).to eq 'http://www.example.org/test.jpg'
            expect(response_env['Rack-Middleware-Grover']).to eq 'true'
          end
        end
      end

      context 'when not requesting a PDF/PNG or JPEG' do
        it 'keeps the original PATH_INFO and not change to REQUEST_URI' do
          get 'http://www.example.org/test.html'
          # env state sent to downstream middleware
          expect(request_env['PATH_INFO']).to eq '/test.html'
          expect(request_env).not_to have_key 'REQUEST_URI'
          expect(request_env).not_to have_key 'Rack-Middleware-Grover'

          # env state bubbling back up
          expect(response_env['PATH_INFO']).to eq '/test.html'
          expect(response_env).not_to have_key 'REQUEST_URI'
          expect(response_env).not_to have_key 'Rack-Middleware-Grover'
        end
      end
    end

    describe 'caching' do
      context 'when requesting a PDF' do
        it 'deletes the cache headers' do
          get 'http://www.example.org/test.pdf'
          expect(last_response.headers).not_to have_key 'ETag'
          expect(last_response.headers).not_to have_key 'Cache-Control'
        end

        context 'when app configuration has PDF middleware disabled' do
          before { allow(Grover.configuration).to receive(:use_pdf_middleware).and_return false }

          it 'returns the cache headers' do
            get 'http://www.example.org/test.pdf'
            expect(last_response.headers['ETag']).to eq 'foo'
            expect(last_response.headers['Cache-Control']).to eq 'max-age=2592000, public'
          end
        end
      end

      context 'when requesting a PNG' do
        it 'returns the cache headers' do
          get 'http://www.example.org/test.png'
          expect(last_response.headers['ETag']).to eq 'foo'
          expect(last_response.headers['Cache-Control']).to eq 'max-age=2592000, public'
        end

        context 'when app configuration has PNG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

          it 'deletes the cache headers' do
            get 'http://www.example.org/test.png'
            expect(last_response.headers).not_to have_key 'ETag'
            expect(last_response.headers).not_to have_key 'Cache-Control'
          end
        end
      end

      context 'when requesting a JPEG' do
        it 'returns the cache headers' do
          get 'http://www.example.org/test.jpeg'
          expect(last_response.headers['ETag']).to eq 'foo'
          expect(last_response.headers['Cache-Control']).to eq 'max-age=2592000, public'
        end

        context 'when app configuration has JPEG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

          it 'deletes the cache headers for JPEG' do
            get 'http://www.example.org/test.jpeg'
            expect(last_response.headers).not_to have_key 'ETag'
            expect(last_response.headers).not_to have_key 'Cache-Control'
          end

          it 'deletes the cache headers for JPG' do
            get 'http://www.example.org/test.jpg'
            expect(last_response.headers).not_to have_key 'ETag'
            expect(last_response.headers).not_to have_key 'Cache-Control'
          end
        end
      end

      context 'when not requesting a PDF, PNG or JPEG' do
        it 'returns the cache headers' do
          get 'http://www.example.org/test'
          expect(last_response.headers['ETag']).to eq 'foo'
          expect(last_response.headers['Cache-Control']).to eq 'max-age=2592000, public'
        end
      end
    end

    describe 'response' do
      context 'when response is a Rack::Response' do
        let(:response) { Rack::Response.new(['Rackalicious'], 200) }

        it 'returns response as PDF' do
          get 'http://www.example.org/test.pdf'
          expect(last_response.headers['Content-Type']).to eq 'application/pdf'
          expect(last_response.body.bytesize).to eq Grover.new('Rackalicious').to_pdf.bytesize
        end

        context 'when app configuration has PDF middleware disabled' do
          before { allow(Grover.configuration).to receive(:use_pdf_middleware).and_return false }

          it 'returns response as text (original)' do
            get 'http://www.example.org/test.pdf'
            expect(last_response.headers['Content-Type']).to eq 'text/html'
            expect(last_response.body).to eq 'Rackalicious'
          end
        end

        it 'returns PNG response as text (original)' do
          get 'http://www.example.org/test.png'
          expect(last_response.headers['Content-Type']).to eq 'text/html'
          expect(last_response.body).to eq 'Rackalicious'
        end

        context 'when app configuration has PNG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

          it 'returns response as PNG' do
            get 'http://www.example.org/test.png'
            expect(last_response.headers['Content-Type']).to eq 'image/png'
            expect(last_response.body.bytesize).to eq Grover.new('Rackalicious').to_png.bytesize
          end
        end

        it 'returns JPEG response as text (original)' do
          get 'http://www.example.org/test.jpeg'
          expect(last_response.headers['Content-Type']).to eq 'text/html'
          expect(last_response.body).to eq 'Rackalicious'
        end

        it 'returns JPG response as text (original)' do
          get 'http://www.example.org/test.jpg'
          expect(last_response.headers['Content-Type']).to eq 'text/html'
          expect(last_response.body).to eq 'Rackalicious'
        end

        context 'when app configuration has JPEG middleware enabled' do
          before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

          it 'returns response as JPEG' do
            get 'http://www.example.org/test.jpeg'
            expect(last_response.headers['Content-Type']).to eq 'image/jpeg'
            expect(last_response.body.bytesize).to eq Grover.new('Rackalicious').to_jpeg.bytesize
          end

          it 'returns response as JPG' do
            get 'http://www.example.org/test.jpg'
            expect(last_response.headers['Content-Type']).to eq 'image/jpeg'
            expect(last_response.body.bytesize).to eq Grover.new('Rackalicious').to_jpeg.bytesize
          end
        end
      end

      context 'when response has multiple parts' do
        let(:response) { ['Part 1', 'Part 2'] }

        it 'returns response as PDF' do
          get 'http://www.example.org/test.pdf'
          expect(last_response.headers['Content-Type']).to eq 'application/pdf'
          expect(last_response.body.bytesize).to eq Grover.new('Part 1Part 2').to_pdf.bytesize
        end
      end
    end

    describe 'preprocessor' do
      it 'calls to the HTML preprocessor with the original HTML' do
        allow(Grover::HTMLPreprocessor).to(
          receive(:process).
            with('Grover McGroveryface', 'http://www.example.org/', 'http').
            and_return('Processed McProcessyface')
        )
        expect(Grover::HTMLPreprocessor).to(
          receive(:process).
            with('Grover McGroveryface', 'http://www.example.org/', 'http')
        )
        get 'http://www.example.org/test.pdf'
        expect(last_response.body.bytesize).to eq Grover.new('Processed McProcessyface').to_pdf.bytesize
      end

      context 'with root_url specified as an argument' do
        subject(:mock_app) do
          builder = Rack::Builder.new
          builder.use described_class, root_url: 'http://example.com/'
          builder.run downstream
          builder.to_app
        end

        it 'calls to the HTML preprocessor with the original HTML and the specified root_url' do
          allow(Grover::HTMLPreprocessor).to(
            receive(:process).
              with('Grover McGroveryface', 'http://example.com/', 'http').
              and_return('Processed McProcessyface')
          )
          expect(Grover::HTMLPreprocessor).to(
            receive(:process).
              with('Grover McGroveryface', 'http://example.com/', 'http')
          )
          get 'http://www.example.org/test.pdf'
          expect(last_response.body.bytesize).to eq Grover.new('Processed McProcessyface').to_pdf.bytesize
        end
      end

      context 'when the response contains relative paths' do
        let(:response) { ['src="/asdf"'] }

        context 'with root_url specified via middleware args' do
          subject(:mock_app) do
            builder = Rack::Builder.new
            builder.use described_class, root_url: 'http://example.com/'
            builder.run downstream
            builder.to_app
          end

          it 'uses the specified root_url' do
            get 'http://www.example.org/test.pdf'
            expect(last_response.body.bytesize).to eq Grover.new('src="http://example.com/asdf"').to_pdf.bytesize
          end

          context 'when the root_url is also set in configuration' do
            before { allow(Grover.configuration).to receive(:root_url).and_return 'http://other.domain/' }

            it 'uses the specified root_url in the middleware initializer' do
              get 'http://www.example.org/test.pdf'
              expect(last_response.body.bytesize).to eq Grover.new('src="http://example.com/asdf"').to_pdf.bytesize
            end
          end
        end

        context 'with root_url set in configuration' do
          before { allow(Grover.configuration).to receive(:root_url).and_return 'http://example.com/' }

          it 'uses the specified root_url' do
            get 'http://www.example.org/test.pdf'
            expect(last_response.body.bytesize).to eq Grover.new('src="http://example.com/asdf"').to_pdf.bytesize
          end
        end

        context 'without root_url specified' do
          it 'uses the detected root_url (request url)' do
            get 'http://www.example.org/test.pdf'
            expect(last_response.body.bytesize).to eq Grover.new('src="http://www.example.org/asdf"').to_pdf.bytesize
          end
        end
      end
    end

    describe 'pdf conversion' do
      let(:grover) { instance_double Grover, show_front_cover?: false, show_back_cover?: false }

      it 'passes through the request URL (sans extension) to Grover' do
        allow(Grover).to(
          receive(:new).
            with('Grover McGroveryface', display_url: 'http://www.example.org/test').
            and_return(grover)
        )
        allow(grover).to receive(:to_pdf).with(no_args).and_return 'A converted PDF'
        expect(Grover).to receive(:new).with('Grover McGroveryface', display_url: 'http://www.example.org/test')
        expect(grover).to receive(:to_pdf).with(no_args)
        get 'http://www.example.org/test.pdf'
        expect(last_response.body).to eq 'A converted PDF'
      end

      { 'key' => 'value', 'escaped' => '%26%3D%3D' }.each do |k, v|
        it 'passes cookies to Grover' do
          allow(Grover).to receive(:new).with(
            'Grover McGroveryface',
            display_url: 'http://www.example.org/test',
            cookies: [{ domain: 'www.example.org', name: k, value: v }]
          ).and_return(grover)
          allow(grover).to receive(:to_pdf).with(no_args).and_return 'A converted PDF'
          expect(Grover).to receive(:new).with(
            'Grover McGroveryface',
            display_url: 'http://www.example.org/test',
            cookies: [{ domain: 'www.example.org', name: k, value: v }]
          )
          expect(grover).to receive(:to_pdf).with(no_args)
          get 'http://www.example.org/test.pdf', nil, 'HTTP_COOKIE' => "#{k}=#{v}"
          expect(last_response.body).to eq 'A converted PDF'
        end
      end

      context 'when app configuration has PDF middleware disabled' do
        before { allow(Grover.configuration).to receive(:use_pdf_middleware).and_return false }

        it 'doesnt call to Grover' do
          expect(Grover).not_to receive(:new)
          get 'http://www.example.org/test.pdf'
          expect(last_response.body).to eq 'Grover McGroveryface'
        end
      end
    end

    describe 'png conversion' do
      let(:grover) { instance_double Grover, show_front_cover?: false, show_back_cover?: false }

      it 'doesnt call to Grover' do
        expect(Grover).not_to receive(:new)
        get 'http://www.example.org/test.png'
        expect(last_response.body).to eq 'Grover McGroveryface'
      end

      context 'when app configuration has PNG middleware enabled' do
        before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

        it 'passes through the request URL (sans extension) to Grover' do
          allow(Grover).to(
            receive(:new).
              with('Grover McGroveryface', display_url: 'http://www.example.org/test').
              and_return(grover)
          )
          allow(grover).to receive(:to_png).with(no_args).and_return 'A converted PNG'
          expect(Grover).to receive(:new).with('Grover McGroveryface', display_url: 'http://www.example.org/test')
          expect(grover).to receive(:to_png).with(no_args)
          get 'http://www.example.org/test.png'
          expect(last_response.body).to eq 'A converted PNG'
        end
      end
    end

    describe 'jpeg conversion' do
      let(:grover) { instance_double Grover, show_front_cover?: false, show_back_cover?: false }

      it 'doesnt call to Grover' do
        expect(Grover).not_to receive(:new)
        get 'http://www.example.org/test.jpeg'
        expect(last_response.body).to eq 'Grover McGroveryface'
      end

      context 'when app configuration has JPEG middleware enabled' do
        before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

        it 'passes through the request URL (sans extension) to Grover' do
          allow(Grover).to(
            receive(:new).
              with('Grover McGroveryface', display_url: 'http://www.example.org/test').
              and_return(grover)
          )
          allow(grover).to receive(:to_jpeg).with(no_args).and_return 'A converted JPEG'
          expect(Grover).to receive(:new).with('Grover McGroveryface', display_url: 'http://www.example.org/test')
          expect(grover).to receive(:to_jpeg).with(no_args)
          get 'http://www.example.org/test.jpeg'
          expect(last_response.body).to eq 'A converted JPEG'
        end
      end
    end

    describe 'front and back cover pages' do
      let(:pdf_reader) { PDF::Reader.new pdf_io }
      let(:pdf_io) { StringIO.new last_response.body }

      before { get 'http://www.example.org/test.pdf' }

      context 'when the downstream response includes front cover page configuration' do
        let(:downstream) do
          lambda do |env|
            response =
              if env['PATH_INFO'] == '/front/page/meta'
                <<-HTML
                  <p>This is the cover page with params:</p>
                  <p>Query string: #{env['QUERY_STRING']}</p>
                  <p>Parameters: #{env['action_dispatch.request.parameters']}</p>
                  <p>Query params: #{env['action_dispatch.request.query_parameters']}</p>
                  <p>Request params: #{env['action_dispatch.request.request_parameters']}</p>
                HTML
              else
                # Can't directly test the helpers for these as they're in Rails, but we can
                # test that they're not persisted in the subsequent cover page request
                env['action_dispatch.request.parameters'] = 'Original params' # Overall Rails param cache
                env['action_dispatch.request.query_parameters'] = 'Original query params' # get params cache
                env['action_dispatch.request.request_parameters'] = 'Original request params' # post params cache
                Grover::Utils.squish <<-HTML
                  <html>
                    <head>
                      <title>Paaage</title>
                      <meta name="grover-front_cover_path" content="/front/page/meta?queryparam=baz" />
                    </head>
                    <body>
                      <h1>Hey there</h1>
                    </body>
                  </html>
                HTML
              end

            [200, headers.merge('Content-Length' => response.length.to_s), [response]]
          end
        end

        it { expect(pdf_reader.page_count).to eq 2 }

        it 'contains expected first page text' do
          expect(Grover::Utils.squish(pdf_reader.pages[0].text)).to eq Grover::Utils.squish <<-HTML
            This is the cover page with params:
            Query string: queryparam=baz
            Parameters:
            Query params:
            Request params:
          HTML
        end

        it { expect(Grover::Utils.squish(pdf_reader.pages[1].text)).to eq 'Hey there' }
      end

      context 'when the downstream response includes back cover page configuration' do
        let(:downstream) do
          lambda do |env|
            response =
              if env['PATH_INFO'] == '/back/page/meta'
                "This is the back page with params #{env['QUERY_STRING']}"
              else
                Grover::Utils.squish <<-HTML
                  <html>
                    <head>
                      <title>Paaage</title>
                      <meta name="grover-back_cover_path" content="/back/page/meta?anotherquery=foo" />
                    </head>
                    <body>
                      <h1>Hey there</h1>
                    </body>
                  </html>
                HTML
              end

            [200, headers.merge('Content-Length' => response.length.to_s), [response]]
          end
        end

        it { expect(pdf_reader.page_count).to eq 2 }
        it { expect(Grover::Utils.squish(pdf_reader.pages[0].text)).to eq 'Hey there' }

        it 'contains expected second page text' do
          expect(Grover::Utils.squish(pdf_reader.pages[1].text)).to(
            eq('This is the back page with params anotherquery=foo')
          )
        end
      end
    end

    describe '#grover_request?' do
      it 'does not get stuck rendering each request as pdf' do
        # renders html by default
        response = get 'http://www.example.org/test'
        expect(response.content_type).to eq 'text/html'

        response = get 'http://www.example.org/test.html'
        expect(response.content_type).to eq 'text/html'

        response = get 'http://www.example.org/test.pdf'
        expect(response.content_type).to eq 'application/pdf'

        response = get 'http://www.example.org/test'
        expect(response.content_type).to eq 'text/html'
      end

      context 'when app configuration has PDF middleware disabled' do
        before { allow(Grover.configuration).to receive(:use_pdf_middleware).and_return false }

        it 'does not handle pdf requests' do
          # renders html by default
          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.pdf'
          expect(response.content_type).to eq 'text/html'
        end
      end

      it 'does not handle png requests' do
        # renders html by default
        response = get 'http://www.example.org/test'
        expect(response.content_type).to eq 'text/html'

        response = get 'http://www.example.org/test.png'
        expect(response.content_type).to eq 'text/html'
      end

      context 'when app configuration has PNG middleware enabled' do
        before { allow(Grover.configuration).to receive(:use_png_middleware).and_return true }

        it 'does not get stuck rendering each request as png' do
          # renders html by default
          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.html'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.png'
          expect(response.content_type).to eq 'image/png'

          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'
        end
      end

      it 'does not handle jpeg requests' do
        # renders html by default
        response = get 'http://www.example.org/test'
        expect(response.content_type).to eq 'text/html'

        response = get 'http://www.example.org/test.jpeg'
        expect(response.content_type).to eq 'text/html'
      end

      it 'does not handle jpg requests' do
        # renders html by default
        response = get 'http://www.example.org/test'
        expect(response.content_type).to eq 'text/html'

        response = get 'http://www.example.org/test.jpg'
        expect(response.content_type).to eq 'text/html'
      end

      context 'when app configuration has JPEG middleware enabled' do
        before { allow(Grover.configuration).to receive(:use_jpeg_middleware).and_return true }

        it 'does not get stuck rendering each request as jpeg' do
          # renders html by default
          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.html'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.jpeg'
          expect(response.content_type).to eq 'image/jpeg'

          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'
        end

        it 'does not get stuck rendering each request as jpg' do
          # renders html by default
          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.html'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/test.jpg'
          expect(response.content_type).to eq 'image/jpeg'

          response = get 'http://www.example.org/test'
          expect(response.content_type).to eq 'text/html'
        end
      end
    end

    describe '#ignore_path?' do
      context 'when app configuration has a String for ignore_path' do
        before { allow(Grover.configuration).to receive(:ignore_path).and_return '/foo/bar' }

        it 'request is ignored when the request path starts with the ignore_path' do
          response = get 'http://www.example.org/foo/bar/baz'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foo/bar/baz.pdf'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foo/baz/bar'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foo/baz/bar.pdf'
          expect(response.content_type).to eq 'application/pdf'
        end
      end

      context 'when app configuration has a Regexp for ignore_path' do
        before { allow(Grover.configuration).to receive(:ignore_path).and_return %r{foo/bar} }

        it 'request is ignored when the request path matches the ignore_path' do
          response = get 'http://www.example.org/baz/foo/bar/baz'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/baz/foo/bar/baz.pdf'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/bar/foo/baz/bar'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/bar/foo/baz/bar.pdf'
          expect(response.content_type).to eq 'application/pdf'
        end
      end

      context 'when app configuration has a Proc for ignore_path' do
        before { allow(Grover.configuration).to receive(:ignore_path).and_return(->(path) { path.include? 'baz' }) }

        it 'request is ignored when the request path passed to the proc defined in ignore_path returns true' do
          response = get 'http://www.example.org/foobazbar'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foobazbar.pdf'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foobarbar'
          expect(response.content_type).to eq 'text/html'

          response = get 'http://www.example.org/foobarbar.pdf'
          expect(response.content_type).to eq 'application/pdf'
        end
      end
    end

    describe '#ignore_request?' do
      context 'when app configuration has a Proc for ignore_request' do
        context 'with a hostname' do
          before do
            allow(Grover.configuration).to(
              receive(:ignore_request).
                and_return(->(req) { req.host == 'www.example.org' })
            )
          end

          it 'request is ignored when the request passed to the proc defined in ignore_request returns true' do
            response = get 'http://www.example.org/foobazbar'
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.example.org/foobazbar.pdf'
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.therealexample.org/foobazbar'
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.therealexample.org/foobazbar.pdf'
            expect(response.content_type).to eq 'application/pdf'
          end
        end

        context 'with a custom header' do
          before do
            allow(Grover.configuration).to(
              receive(:ignore_request).
                and_return(->(req) { req.has_header?('X-BLOCK') })
            )
          end

          it 'request is ignored when the request passed to the proc defined in ignore_request returns true' do
            response = get 'http://www.example.org/foobazbar', {}, { 'X-BLOCK' => '1' }
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.example.org/foobazbar.pdf', {}, { 'X-BLOCK' => '1' }
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.therealexample.org/foobazbar'
            expect(response.content_type).to eq 'text/html'

            response = get 'http://www.therealexample.org/foobazbar.pdf'
            expect(response.content_type).to eq 'application/pdf'
          end
        end
      end
    end

    context 'with a downstream app that can respond to different paths' do
      let(:downstream) do
        lambda do |env|
          response =
            case env['PATH_INFO']
            when '/test1' then 'Test1 page contents'
            when '/test2' then 'Test2 page contents'
            else 'Default page contents'
            end

          [200, headers.merge('Content-Length' => response.length.to_s), [response]]
        end
      end

      it 'does not cache any results from previous requests' do
        # Non-PDF request
        get 'http://www.example.org/test1'
        expect(last_response.body).to eq 'Test1 page contents'

        # PDF request for 'test 1'
        get 'http://www.example.org/test1.pdf'
        expect(last_response.body).to be_a_pdf
        check_response_pdf contents: 'Test1 page contents'

        # PDF request for 'test 2'
        get 'http://www.example.org/test2.pdf'
        expect(last_response.body).to be_a_pdf
        check_response_pdf contents: 'Test2 page contents'

        # Non-PDF request
        get 'http://www.example.org/test1'
        expect(last_response.body).to eq 'Test1 page contents'
      end

      def check_response_pdf(contents:)
        pdf_reader = pdf_reader_from_response
        expect(pdf_reader.page_count).to eq 1
        expect(Grover::Utils.squish(pdf_reader.pages.first.text)).to eq contents
      end

      def pdf_reader_from_response
        pdf_io = StringIO.new last_response.body
        PDF::Reader.new pdf_io
      end

      def be_a_pdf
        match(/^%PDF-1\.4/)
      end
    end

    describe 'thread safety' do
      let(:thread_count) { 30 }
      let(:extensions) { Array.new(thread_count) { rand > 0.5 ? 'html' : 'pdf' } }
      let(:response_content_types) { {} }
      let(:threads) do
        (0...thread_count).map do |i|
          Thread.new do
            response = get "http://www.example.org/test.#{extensions[i]}"
            response_content_types[i] = response.content_type
          end
        end
      end

      before { mock_app }

      it 'is threadsafe' do
        threads.each(&:join)

        extensions.each_with_index do |extension, index|
          response_content_type = response_content_types[index]

          case extension
          when 'html'
            expect(response_content_type).to eq 'text/html'
          when 'pdf'
            expect(response_content_type).to eq 'application/pdf'
          end
        end
      end
    end
  end
end