# frozen_string_literal: true require 'spec_helper' module Bullet describe Rack do let(:middleware) { Bullet::Rack.new app } let(:app) { Support::AppDouble.new } context '#html_request?' do it 'should be true if Content-Type is text/html and http body contains html tag' do headers = { 'Content-Type' => 'text/html' } response = double(body: '') expect(middleware).to be_html_request(headers, response) end it 'should be true if Content-Type is text/html and http body contains html tag with attributes' do headers = { 'Content-Type' => 'text/html' } response = double(body: "") expect(middleware).to be_html_request(headers, response) end it 'should be false if there is no Content-Type header' do headers = {} response = double(body: '') expect(middleware).not_to be_html_request(headers, response) end it 'should be false if Content-Type is javascript' do headers = { 'Content-Type' => 'text/javascript' } response = double(body: '') expect(middleware).not_to be_html_request(headers, response) end end context 'empty?' do it 'should be false if response is a string and not empty' do response = double(body: '') expect(middleware).not_to be_empty(response) end it 'should be false if response is not found' do response = ['Not Found'] expect(middleware).not_to be_empty(response) end it 'should be true if response body is empty' do response = double(body: '') expect(middleware).to be_empty(response) end it 'should be true if no response body' do response = double() expect(middleware).to be_empty(response) end end context '#call' do context 'when Bullet is enabled' do it 'should return original response body' do expected_response = Support::ResponseDouble.new 'Actual body' app.response = expected_response _, _, response = middleware.call({}) expect(response).to eq(expected_response) end it 'should change response body if notification is active' do expect(Bullet).to receive(:notification?).and_return(true) expect(Bullet).to receive(:console_enabled?).and_return(true) expect(Bullet).to receive(:gather_inline_notifications).and_return('') expect(Bullet).to receive(:perform_out_of_channel_notifications) _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq('56') expect(response).to eq(%w[]) end it 'should set the right Content-Length if response body contains accents' do response = Support::ResponseDouble.new response.body = 'é' app.response = response expect(Bullet).to receive(:notification?).and_return(true) allow(Bullet).to receive(:console_enabled?).and_return(true) expect(Bullet).to receive(:gather_inline_notifications).and_return('') _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq('58') end context 'with injection notifiers' do before do expect(Bullet).to receive(:notification?).and_return(true) allow(Bullet).to receive(:gather_inline_notifications).and_return('') allow(middleware).to receive(:xhr_script).and_return('') allow(middleware).to receive(:footer_note).and_return('footer') expect(Bullet).to receive(:perform_out_of_channel_notifications) end it 'should change response body if add_footer is true' do expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true) _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s) expect(response).to eq(%w[footer]) end it 'should change response body for html safe string if add_footer is true' do expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true) app.response = Support::ResponseDouble.new.tap do |response| response.body = ActiveSupport::SafeBuffer.new('') end _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s) expect(response).to eq(%w[footer]) end it 'should add the footer-text header for non-html requests when add_footer is true' do allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true) allow(Bullet).to receive(:footer_info).and_return(['footer text']) app.headers = { 'Content-Type' => 'application/json' } _, headers, _response = middleware.call({}) expect(headers).to include('X-bullet-footer-text' => '["footer text"]') end it 'should change response body if console_enabled is true' do expect(Bullet).to receive(:console_enabled?).and_return(true) _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq('56') expect(response).to eq(%w[]) end it 'should change response body for html safe string if console_enabled is true' do expect(Bullet).to receive(:console_enabled?).and_return(true) app.response = Support::ResponseDouble.new.tap do |response| response.body = ActiveSupport::SafeBuffer.new('') end _, headers, response = middleware.call('Content-Type' => 'text/html') expect(headers['Content-Length']).to eq('56') expect(response).to eq(%w[]) end it 'should add headers for non-html requests when console_enabled is true' do allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true) allow(Bullet).to receive(:text_notifications).and_return(['text notifications']) app.headers = { 'Content-Type' => 'application/json' } _, headers, _response = middleware.call({}) expect(headers).to include('X-bullet-console-text' => '["text notifications"]') end it "shouldn't change response body unnecessarily" do expected_response = Support::ResponseDouble.new 'Actual body' app.response = expected_response _, _, response = middleware.call({}) expect(response).to eq(expected_response) end it "shouldn't add headers unnecessarily" do app.headers = { 'Content-Type' => 'application/json' } _, headers, _response = middleware.call({}) expect(headers).not_to include('X-bullet-footer-text') expect(headers).not_to include('X-bullet-console-text') end context 'when skip_http_headers is enabled' do before do allow(Bullet).to receive(:skip_http_headers).and_return(true) end it 'should include the footer but not the xhr script tag if add_footer is true' do expect(Bullet).to receive(:add_footer).at_least(:once).and_return(true) _, headers, response = middleware.call({}) expect(headers['Content-Length']).to eq((56 + middleware.send(:footer_note).length).to_s) expect(response).to eq(%w[footer]) end it 'should not include the xhr script tag if console_enabled is true' do expect(Bullet).to receive(:console_enabled?).and_return(true) _, headers, response = middleware.call({}) expect(headers['Content-Length']).to eq('56') expect(response).to eq(%w[]) end it 'should not add the footer-text header for non-html requests when add_footer is true' do allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true) app.headers = { 'Content-Type' => 'application/json' } _, headers, _response = middleware.call({}) expect(headers).not_to include('X-bullet-footer-text') end it 'should not add headers for non-html requests when console_enabled is true' do allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true) app.headers = { 'Content-Type' => 'application/json' } _, headers, _response = middleware.call({}) expect(headers).not_to include('X-bullet-console-text') end end end context 'when skip_html_injection is enabled' do it 'should not try to inject html' do expected_response = Support::ResponseDouble.new 'Actual body' app.response = expected_response allow(Bullet).to receive(:notification?).and_return(true) allow(Bullet).to receive(:skip_html_injection?).and_return(true) expect(Bullet).to receive(:gather_inline_notifications).never expect(middleware).to receive(:xhr_script).never expect(Bullet).to receive(:perform_out_of_channel_notifications) _, _, response = middleware.call('Content-Type' => 'text/html') expect(response).to eq(expected_response) end end end context 'when Bullet is disabled' do before(:each) { allow(Bullet).to receive(:enable?).and_return(false) } it 'should not call Bullet.start_request' do expect(Bullet).not_to receive(:start_request) middleware.call({}) end end end context '#set_header' do it 'should truncate headers to under 8kb' do long_header = ['a' * 1_024] * 10 expected_res = (['a' * 1_024] * 7).to_json expect(middleware.set_header({}, 'Dummy-Header', long_header)).to eq(expected_res) end end describe '#response_body' do let(:response) { double } let(:body_string) { 'My Body' } context 'when `response` responds to `body`' do before { allow(response).to receive(:body).and_return(body) } context 'when `body` returns an Array' do let(:body) { [body_string, 'random string'] } it 'should return the plain body string' do expect(middleware.response_body(response)).to eq body_string end end context 'when `body` does not return an Array' do let(:body) { body_string } it 'should return the plain body string' do expect(middleware.response_body(response)).to eq body_string end end end context 'when `response` does not respond to `body`' do before { allow(response).to receive(:first).and_return(body_string) } it 'should return the plain body string' do expect(middleware.response_body(response)).to eq body_string end end begin require 'rack/files' context 'when `response` is a Rack::Files::Iterator' do let(:response) { instance_double(::Rack::Files::Iterator) } before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } } it 'should return nil' do expect(middleware.response_body(response)).to be_nil end end rescue LoadError end end end end