<commit_before>RSpec.describe Airbrake::SyncSender do
  subject(:sync_sender) { described_class.new }

  before do
    Airbrake::Config.instance = Airbrake::Config.new(
      project_id: 1, project_key: 'banana',
    )
  end

  describe "#send" do
    let(:promise) { Airbrake::Promise.new }

    let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
    let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }

    before { stub_request(:post, endpoint).to_return(body: '{}') }

    it "sets the Content-Type header to JSON" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: { 'Content-Type' => 'application/json' },
        ),
      ).to have_been_made.once
    end

    it "sets the User-Agent header to the notifier slug" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: {
            'User-Agent' => %r{
              airbrake-ruby/\d+\.\d+\.\d+(\.rc\.\d+)?\sRuby/\d+\.\d+\.\d+
            }x,
          },
        ),
      ).to have_been_made.once
    end

    it "sets the Authorization header to the project key" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: { 'Authorization' => 'Bearer banana' },
        ),
      ).to have_been_made.once
    end

    it "catches exceptions raised while sending" do
      https = double("foo")
      # rubocop:disable RSpec/SubjectStub
      allow(sync_sender).to receive(:build_https).and_return(https)
      # rubocop:enable RSpec/SubjectStub

      allow(https).to receive(:request).and_raise(StandardError.new('foo'))

      expect(sync_sender.send({}, promise)).to be_an(Airbrake::Promise)
      expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
    end

    it "logs exceptions raised while sending" do
      allow(Airbrake::Loggable.instance).to receive(:error)

      https = double("foo")

      # rubocop:disable RSpec/SubjectStub
      allow(sync_sender).to receive(:build_https).and_return(https)
      # rubocop:enable RSpec/SubjectStub

      allow(https).to receive(:request).and_raise(StandardError.new('foo'))

      sync_sender.send({}, promise)

      expect(Airbrake::Loggable.instance).to have_received(:error).with(
        /HTTP error: foo/,
      )
    end

    context "when request body is nil" do
      # rubocop:disable RSpec/MultipleExpectations
      it "doesn't send data" do
        allow(Airbrake::Loggable.instance).to receive(:error)

        allow_any_instance_of(Airbrake::Truncator)
          .to receive(:reduce_max_size).and_return(0)

        encoded = Base64.encode64("\xD3\xE6\xBC\x9D\xBA").encode!('ASCII-8BIT')
        bad_string = Base64.decode64(encoded)

        ex = AirbrakeTestError.new
        backtrace = []
        10.times { backtrace << "bin/rails:3:in `<#{bad_string}>'" }
        ex.set_backtrace(backtrace)

        notice = Airbrake::Notice.new(ex)

        expect(sync_sender.send(notice, promise)).to be_an(Airbrake::Promise)
        expect(promise.value)
          .to match('error' => '**Airbrake: data was not sent because of missing body')

        expect(Airbrake::Loggable.instance).to have_received(:error).with(
          /data was not sent/,
        )
        expect(Airbrake::Loggable.instance).to have_received(:error).with(
          /truncation failed/,
        )
      end
      # rubocop:enable RSpec/MultipleExpectations
    end

    context "when IP is rate limited" do
      let(:endpoint) { %r{https://api.airbrake.io/api/v3/projects/1/notices} }

      before do
        stub_request(:post, endpoint).to_return(
          status: 429,
          body: '{"message":"IP is rate limited"}',
          headers: { 'X-RateLimit-Delay' => '1' },
        )
      end

      # rubocop:disable RSpec/MultipleExpectations
      it "returns error" do
        p1 = Airbrake::Promise.new
        sync_sender.send({}, p1)
        expect(p1.value).to match('error' => '**Airbrake: IP is rate limited')

        p2 = Airbrake::Promise.new
        sync_sender.send({}, p2)
        expect(p2.value).to match('error' => '**Airbrake: IP is rate limited')

        # Wait for X-RateLimit-Delay and then make a new request to make sure p2
        # was ignored (no request made for it).
        sleep 1

        p3 = Airbrake::Promise.new
        sync_sender.send({}, p3)
        expect(p3.value).to match('error' => '**Airbrake: IP is rate limited')

        expect(a_request(:post, endpoint)).to have_been_made.twice
      end
      # rubocop:enable RSpec/MultipleExpectations
    end

    context "when the provided method is :put" do
      before { stub_request(:put, endpoint).to_return(status: 200, body: '') }

      it "PUTs the request" do
        sender = described_class.new(:put)
        sender.send({}, promise)
        expect(a_request(:put, endpoint)).to have_been_made
      end
    end

    context "when the provided method is :post" do
      it "POSTs the request" do
        sender = described_class.new(:post)
        sender.send({}, promise)
        expect(a_request(:post, endpoint)).to have_been_made
      end
    end
  end
end
<commit_msg>rubocop: fix offences of RSpec/VerifiedDoubles cop<commit_after>RSpec.describe Airbrake::SyncSender do
  subject(:sync_sender) { described_class.new }

  before do
    Airbrake::Config.instance = Airbrake::Config.new(
      project_id: 1, project_key: 'banana',
    )
  end

  describe "#send" do
    let(:promise) { Airbrake::Promise.new }

    let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
    let(:endpoint) { 'https://api.airbrake.io/api/v3/projects/1/notices' }

    before { stub_request(:post, endpoint).to_return(body: '{}') }

    it "sets the Content-Type header to JSON" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: { 'Content-Type' => 'application/json' },
        ),
      ).to have_been_made.once
    end

    it "sets the User-Agent header to the notifier slug" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: {
            'User-Agent' => %r{
              airbrake-ruby/\d+\.\d+\.\d+(\.rc\.\d+)?\sRuby/\d+\.\d+\.\d+
            }x,
          },
        ),
      ).to have_been_made.once
    end

    it "sets the Authorization header to the project key" do
      sync_sender.send({}, promise)
      expect(
        a_request(:post, endpoint).with(
          headers: { 'Authorization' => 'Bearer banana' },
        ),
      ).to have_been_made.once
    end

    it "catches exceptions raised while sending" do
      # rubocop:disable RSpec/VerifiedDoubles
      https = double("foo")
      # rubocop:enable RSpec/VerifiedDoubles

      # rubocop:disable RSpec/SubjectStub
      allow(sync_sender).to receive(:build_https).and_return(https)
      # rubocop:enable RSpec/SubjectStub

      allow(https).to receive(:request).and_raise(StandardError.new('foo'))

      expect(sync_sender.send({}, promise)).to be_an(Airbrake::Promise)
      expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
    end

    it "logs exceptions raised while sending" do
      allow(Airbrake::Loggable.instance).to receive(:error)

      # rubocop:disable RSpec/VerifiedDoubles
      https = double("foo")
      # rubocop:enable RSpec/VerifiedDoubles

      # rubocop:disable RSpec/SubjectStub
      allow(sync_sender).to receive(:build_https).and_return(https)
      # rubocop:enable RSpec/SubjectStub

      allow(https).to receive(:request).and_raise(StandardError.new('foo'))

      sync_sender.send({}, promise)

      expect(Airbrake::Loggable.instance).to have_received(:error).with(
        /HTTP error: foo/,
      )
    end

    context "when request body is nil" do
      # rubocop:disable RSpec/MultipleExpectations
      it "doesn't send data" do
        allow(Airbrake::Loggable.instance).to receive(:error)

        allow_any_instance_of(Airbrake::Truncator)
          .to receive(:reduce_max_size).and_return(0)

        encoded = Base64.encode64("\xD3\xE6\xBC\x9D\xBA").encode!('ASCII-8BIT')
        bad_string = Base64.decode64(encoded)

        ex = AirbrakeTestError.new
        backtrace = []
        10.times { backtrace << "bin/rails:3:in `<#{bad_string}>'" }
        ex.set_backtrace(backtrace)

        notice = Airbrake::Notice.new(ex)

        expect(sync_sender.send(notice, promise)).to be_an(Airbrake::Promise)
        expect(promise.value)
          .to match('error' => '**Airbrake: data was not sent because of missing body')

        expect(Airbrake::Loggable.instance).to have_received(:error).with(
          /data was not sent/,
        )
        expect(Airbrake::Loggable.instance).to have_received(:error).with(
          /truncation failed/,
        )
      end
      # rubocop:enable RSpec/MultipleExpectations
    end

    context "when IP is rate limited" do
      let(:endpoint) { %r{https://api.airbrake.io/api/v3/projects/1/notices} }

      before do
        stub_request(:post, endpoint).to_return(
          status: 429,
          body: '{"message":"IP is rate limited"}',
          headers: { 'X-RateLimit-Delay' => '1' },
        )
      end

      # rubocop:disable RSpec/MultipleExpectations
      it "returns error" do
        p1 = Airbrake::Promise.new
        sync_sender.send({}, p1)
        expect(p1.value).to match('error' => '**Airbrake: IP is rate limited')

        p2 = Airbrake::Promise.new
        sync_sender.send({}, p2)
        expect(p2.value).to match('error' => '**Airbrake: IP is rate limited')

        # Wait for X-RateLimit-Delay and then make a new request to make sure p2
        # was ignored (no request made for it).
        sleep 1

        p3 = Airbrake::Promise.new
        sync_sender.send({}, p3)
        expect(p3.value).to match('error' => '**Airbrake: IP is rate limited')

        expect(a_request(:post, endpoint)).to have_been_made.twice
      end
      # rubocop:enable RSpec/MultipleExpectations
    end

    context "when the provided method is :put" do
      before { stub_request(:put, endpoint).to_return(status: 200, body: '') }

      it "PUTs the request" do
        sender = described_class.new(:put)
        sender.send({}, promise)
        expect(a_request(:put, endpoint)).to have_been_made
      end
    end

    context "when the provided method is :post" do
      it "POSTs the request" do
        sender = described_class.new(:post)
        sender.send({}, promise)
        expect(a_request(:post, endpoint)).to have_been_made
      end
    end
  end
end

{'fraction_non_alphanumeric': 0.13156408808582723, 'fraction_numerical': 0.007340485601355167, 'mean_word_length': 2.5926301555104803, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 23, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7427398', 'n_tokens_mistral': 3460, 'n_tokens_neox': 3336, 'n_words': 711}