require 'spec_helper.rb'

describe Pwwka::Transmitter do

  before(:all) do
    @test_handler = Pwwka::TestHandler.new
    @test_handler.test_setup
  end

  after(:each) { @test_handler.purge_test_queue }
  after(:all) { @test_handler.test_teardown }

  let(:payload)     { { "this" => "that" } }
  let(:routing_key) { "this.that.and.theother" }
  let(:exception) { RuntimeError.new('blow up')}
  let(:logger) { double(Logger) }

  before(:each) do
    @original_logger = Pwwka.configuration.logger
    Pwwka.configuration.logger = logger
    allow(logger).to receive(:info)
    allow(logger).to receive(:warn)
    allow(logger).to receive(:error)
  end

  after(:each) do
    Pwwka.configuration.logger = @original_logger
  end

  describe "#send_message!" do

    context "happy path" do
      it "should send the correct payload" do
        success = Pwwka::Transmitter.new.send_message!(payload, routing_key)
        expect(success).to be_truthy
        received_payload = @test_handler.pop_message.payload
        expect(received_payload["this"]).to eq("that")
        expect(logger).to have_received(:info).with("START Transmitting Message on #{routing_key} -> #{payload}")
        expect(logger).to have_received(:info).with("END Transmitting Message on #{routing_key} -> #{payload}")
      end

      it "should deliver on the expected routing key" do
        success = Pwwka::Transmitter.new.send_message!(payload, routing_key)
        expect(success).to be_truthy
        delivery_info = @test_handler.pop_message.delivery_info
        expect(delivery_info.routing_key).to eq(routing_key)
      end
    end

    it "should blow up if exception raised" do
      expect_any_instance_of(Pwwka::ChannelConnector).to receive(:topic_exchange).and_raise(exception)
      expect {
        Pwwka::Transmitter.new.send_message!(payload, routing_key)
      }.to raise_error(exception)
      expect(logger).to     have_received(:info).with("START Transmitting Message on #{routing_key} -> #{payload}")
      expect(logger).not_to have_received(:info).with("END Transmitting Message on #{routing_key} -> #{payload}")
    end

  end

  describe "#send_delayed_message!" do

    context "happy path" do
      it "should send the correct payload" do
        success = Pwwka::Transmitter.new.send_delayed_message!(payload, routing_key, 1000)
        expect(success).to be_truthy
        expect(@test_handler.test_queue.message_count).to eq(0)
        sleep 5
        expect(@test_handler.test_queue.message_count).to eq(1)
        received_payload = @test_handler.pop_message.payload
        expect(received_payload["this"]).to eq("that")
        expect(logger).to have_received(:info).with("START Transmitting Delayed Message on #{routing_key} -> #{payload}")
        expect(logger).to have_received(:info).with("END Transmitting Delayed Message on #{routing_key} -> #{payload}")
      end

      it "should deliver on the expected routing key" do
        success = Pwwka::Transmitter.new.send_delayed_message!(payload, routing_key, 1)
        expect(success).to be_truthy
        sleep 1
        delivery_info = @test_handler.pop_message.delivery_info
        expect(delivery_info.routing_key).to eq(routing_key)
      end
    end

    it "should blow up if exception raised" do
      expect_any_instance_of(Pwwka::ChannelConnector).to receive(:create_delayed_queue).and_raise(exception)
      expect {
        Pwwka::Transmitter.new.send_delayed_message!(payload, routing_key, 1)
      }.to raise_error(exception)
      expect(logger).to have_received(:info).with("START Transmitting Delayed Message on #{routing_key} -> #{payload}")
      expect(logger).not_to have_received(:info).with("END Transmitting Delayed Message on #{routing_key} -> #{payload}")
    end

    context "delayed not configured" do
      it "should blow up if allow_delayed? is false" do
        expect(@test_handler.channel_connector.configuration).to receive(:allow_delayed?).at_least(:once).and_return(false)
        expect {
          Pwwka::Transmitter.new.send_delayed_message!(payload, routing_key, 1)
        }.to raise_error(Pwwka::ConfigurationError)
      end
    end

  end

  describe "::send_message!" do


    it "should send the correct payload" do
      Pwwka::Transmitter.send_message!(payload, routing_key)
      received_payload = @test_handler.pop_message.payload
      expect(received_payload["this"]).to eq("that")
    end

    it "should return true" do
      expect(Pwwka::Transmitter.send_message!(payload, routing_key)).to eq true
    end

    it "should ignore delay_by parameter (should it?)" do
      Pwwka::Transmitter.send_message!(payload, routing_key, delay_by: 5000)
      received_payload = @test_handler.pop_message.payload
      expect(received_payload["this"]).to eq("that")
    end

    context 'default exception policy' do
      it "should blow up if exception raised" do
        expect(Pwwka::ChannelConnector).to receive(:new).and_raise(exception)
        expect {
          Pwwka::Transmitter.send_message!(payload, routing_key)
        }.to raise_error(exception)
      end
    end

    context 'when on_error: :raise and exception raised' do
      before(:each) { expect(Pwwka::ChannelConnector).to receive(:new).and_raise(exception) }

      it "should blow up" do
        expect {
          Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :raise)
        }.to raise_error(exception)
      end
      it "should not enqueue a resque job" do
        expect(Resque).not_to receive(:enqueue_in)
        expect {
          Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :raise)
        }.to raise_error(exception)
      end
    end

    context 'when on_error: :ignore and exception raised' do
      before :each do
        expect(Pwwka::ChannelConnector).to receive(:new).and_raise("blow up")
      end
      it "should not blow up" do
        Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :ignore)
        # check nothing has been queued
        expect(@test_handler.test_queue.pop.compact.count).to eq(0)
      end
      it "should return false" do
        expect(Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :ignore)).to eql false
      end
      it "should not enqueue a resque job" do
        expect(Resque).not_to receive(:enqueue_in)
        Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :ignore)
      end
    end

    context 'when on_error: :resque and exception raised' do
      before :each do
        allow(Resque).to receive(:enqueue_in)
        expect(Pwwka::ChannelConnector).to receive(:new).and_raise("blow up")
      end
      it "should return false" do
        expect(Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :resque)).to eq false
      end

      it "should enqueue a Resque job if exception raised" do
        expect(Resque).to receive(:enqueue_in).
                              with(0, Pwwka::SendMessageAsyncJob, payload, routing_key)

        Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :resque)
        # check nothing has been queued
        expect(@test_handler.test_queue.pop.compact.count).to eq(0)
      end

      context 'and then resque fails' do
        it 'returns the original exception' do
          expect(Resque).to receive(:enqueue_in).and_raise('blow up in resque')
          expect {
            Pwwka::Transmitter.send_message!(payload, routing_key, on_error: :resque)
          }.to raise_exception('blow up')
        end
      end
    end


    context "delayed message" do

      it "should call send_delayed_message! if requested with delay_by" do
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_delayed_message!)
                                                          .with(payload, routing_key, 2000)
        Pwwka::Transmitter.send_message!(payload, routing_key, delayed: true, delay_by: 2000)
      end

      it "should call send_delayed_message if requested without delay_by" do
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_delayed_message!)
                                                          .with(payload, routing_key)
        Pwwka::Transmitter.send_message!(payload, routing_key, delayed: true)
      end

      it "should not call send_delayed_message if not requested" do
        expect_any_instance_of(Pwwka::Transmitter).not_to receive(:send_delayed_message!)
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_message!)
        Pwwka::Transmitter.send_message_safely(payload, routing_key)
      end

      it "should enqueue a Resque job if exception raised and on_error: :resque" do
        expect(Pwwka::ChannelConnector).to receive(:new).and_raise("blow up")

        expect(Resque).to receive(:enqueue_in).
                              with(2, Pwwka::SendMessageAsyncJob, payload, routing_key)

        Pwwka::Transmitter.send_message!(payload, routing_key, delayed: true, delay_by: 2000, on_error: :resque)
        # check nothing has been queued
        expect(@test_handler.test_queue.pop.compact.count).to eq(0)
      end


      it "should enqueue a Resque job if exception raised and on_error: :resque without delay_by" do
        expect(Pwwka::ChannelConnector).to receive(:new).and_raise("blow up")

        expect(Resque).to receive(:enqueue_in).
                              with(Pwwka::Transmitter::DEFAULT_DELAY_BY_MS/1000, Pwwka::SendMessageAsyncJob, payload, routing_key)

        Pwwka::Transmitter.send_message!(payload, routing_key, delayed: true, on_error: :resque)
        # check nothing has been queued
        expect(@test_handler.test_queue.pop.compact.count).to eq(0)
      end

    end
  end


  describe '::send_message_async' do
    context 'with no delay' do
      it 'queues the message' do
        expect(Resque).to receive(:enqueue_in).
                              with(0, Pwwka::SendMessageAsyncJob, payload, routing_key)
        Pwwka::Transmitter.send_message_async(payload, routing_key)
      end
    end

    context 'with delay' do
      it 'queues the message' do
        expect(Resque).to receive(:enqueue_in).
                              with(3, Pwwka::SendMessageAsyncJob, payload, routing_key)
        Pwwka::Transmitter.send_message_async(payload, routing_key, delay_by_ms: 3000)
      end
    end
  end

  describe "::send_message_safely" do

    it "should send the correct payload" do
      Pwwka::Transmitter.send_message_safely(payload, routing_key)
      received_payload = @test_handler.pop_message.payload
      expect(received_payload["this"]).to eq("that")
    end

    it "should not blow up if exception raised" do
      expect(Pwwka::ChannelConnector).to receive(:new).and_raise("blow up")
      Pwwka::Transmitter.send_message_safely(payload, routing_key)
      # check nothing has been queued
      expect(@test_handler.test_queue.pop.compact.count).to eq(0)
    end

    context "delayed message" do

      it "should call send_delayed_message! if requested with delay_by" do
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_delayed_message!)
                                                          .with(payload, routing_key, 2000)
        Pwwka::Transmitter.send_message_safely(payload, routing_key, delayed: true, delay_by: 2000)
      end

      it "should call send_delayed_message if requested without delay_by" do
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_delayed_message!)
                                                          .with(payload, routing_key)
        Pwwka::Transmitter.send_message_safely(payload, routing_key, delayed: true)
      end

      it "should not call send_delayed_message if not requested" do
        expect_any_instance_of(Pwwka::Transmitter).not_to receive(:send_delayed_message!)
        expect_any_instance_of(Pwwka::Transmitter).to receive(:send_message!)
        Pwwka::Transmitter.send_message_safely(payload, routing_key)
      end

    end

  end

end