require "appsignal/integrations/shoryuken"

describe Appsignal::Integrations::ShoryukenMiddleware do
  class DemoShoryukenWorker
  end

  let(:time) { "2010-01-01 10:01:00UTC" }
  let(:worker_instance) { DemoShoryukenWorker.new }
  let(:queue) { "some-funky-queue-name" }
  let(:sqs_msg) { double(:message_id => "msg1", :attributes => {}) }
  let(:body) { {} }
  let(:options) { {} }
  before { start_agent(:options => options) }
  around { |example| keep_transactions { example.run } }

  def perform_shoryuken_job(&block)
    block ||= lambda {}
    Timecop.freeze(Time.parse(time)) do
      described_class.new.call(
        worker_instance,
        queue,
        sqs_msg,
        body,
        &block
      )
    end
  end

  context "with a performance call" do
    let(:sent_timestamp) { Time.parse("1976-11-18 0:00:00UTC").to_i * 1000 }
    let(:sqs_msg) do
      double(:message_id => "msg1", :attributes => { "SentTimestamp" => sent_timestamp })
    end

    context "with complex argument" do
      let(:body) { { :foo => "Foo", :bar => "Bar" } }

      it "wraps the job in a transaction with the correct params" do
        expect { perform_shoryuken_job }.to change { created_transactions.length }.by(1)

        transaction = last_transaction
        expect(transaction).to have_id
        expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
        expect(transaction).to have_action("DemoShoryukenWorker#perform")
        expect(transaction).to_not have_error
        expect(transaction).to include_event(
          "body" => "",
          "body_format" => Appsignal::EventFormatter::DEFAULT,
          "count" => 1,
          "name" => "perform_job.shoryuken",
          "title" => ""
        )
        expect(transaction).to include_params("foo" => "Foo", "bar" => "Bar")
        expect(transaction).to include_tags(
          "message_id" => "msg1",
          "queue" => queue,
          "SentTimestamp" => sent_timestamp
        )
        expect(transaction).to have_queue_start(sent_timestamp)
        expect(transaction).to be_completed
      end

      context "with parameter filtering" do
        let(:options) { { :filter_parameters => ["foo"] } }

        it "filters selected arguments" do
          perform_shoryuken_job

          expect(last_transaction).to include_params("foo" => "[FILTERED]", "bar" => "Bar")
        end
      end
    end

    context "with a string as an argument" do
      let(:body) { "foo bar" }

      it "handles string arguments" do
        perform_shoryuken_job

        expect(last_transaction).to include_params("params" => body)
      end
    end

    context "with primitive type as argument" do
      let(:body) { 1 }

      it "handles primitive types as arguments" do
        perform_shoryuken_job

        expect(last_transaction).to include_params("params" => body)
      end
    end
  end

  context "with exception" do
    it "sets the exception on the transaction" do
      expect do
        expect do
          perform_shoryuken_job { raise ExampleException, "error message" }
        end.to raise_error(ExampleException)
      end.to change { created_transactions.length }.by(1)

      transaction = last_transaction
      expect(transaction).to have_id
      expect(transaction).to have_action("DemoShoryukenWorker#perform")
      expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
      expect(transaction).to have_error("ExampleException", "error message")
      expect(transaction).to be_completed
    end
  end

  context "with batched jobs" do
    let(:sqs_msg) do
      [
        double(
          :message_id => "msg2",
          :attributes => {
            "SentTimestamp" => (Time.parse("1976-11-18 01:00:00UTC").to_i * 1000).to_s
          }
        ),
        double(
          :message_id => "msg1",
          :attributes => { "SentTimestamp" => sent_timestamp.to_s }
        )
      ]
    end
    let(:body) do
      [
        "foo bar",
        { :id => "123", :foo => "Foo", :bar => "Bar" }
      ]
    end
    let(:sent_timestamp) { Time.parse("1976-11-18 01:00:00UTC").to_i * 1000 }

    it "creates a transaction for the batch" do
      expect do
        perform_shoryuken_job {} # rubocop:disable Lint/EmptyBlock
      end.to change { created_transactions.length }.by(1)

      transaction = last_transaction
      expect(transaction).to have_id
      expect(transaction).to have_action("DemoShoryukenWorker#perform")
      expect(transaction).to have_namespace(Appsignal::Transaction::BACKGROUND_JOB)
      expect(transaction).to_not have_error
      expect(transaction).to include_event(
        "body" => "",
        "body_format" => Appsignal::EventFormatter::DEFAULT,
        "count" => 1,
        "name" => "perform_job.shoryuken",
        "title" => ""
      )
      expect(transaction).to include_params(
        "msg2" => "foo bar",
        "msg1" => { "id" => "123", "foo" => "Foo", "bar" => "Bar" }
      )
      expect(transaction).to include_tags(
        "batch" => true,
        "queue" => "some-funky-queue-name",
        "SentTimestamp" => sent_timestamp.to_s # Earliest/oldest timestamp from messages
      )
      # Queue time based on earliest/oldest timestamp from messages
      expect(transaction).to have_queue_start(sent_timestamp)
    end
  end
end
