require "spec_helper"

describe Agent::Pop do

  context "in its basic operation" do
    before do
      @pop = Agent::Pop.new
      @ack = channel!(Time)
    end

    it "should close" do
      expect(@pop).not_to be_closed
      @pop.close
      expect(@pop).to be_closed
    end

    it "should run multiple times" do
      @pop.send{1}
      expect(@pop).to be_received
      @pop.send{2}
      expect(@pop.object).to eq(2)
    end

    it "should continue when received" do
      go!{ @pop.wait; @ack.send(Time.now) }
      sleep 0.2
      @pop.send{1}

      s, _ = @ack.receive

      expect(Time.now - s).to be_within(0.2).of(0)
    end

    it "should continue when closed" do
      go!{ @pop.wait; @ack.send(Time.now) }
      sleep 0.2
      @pop.close

      s, _ = @ack.receive

      expect(Time.now - s).to be_within(0.2).of(0)
    end

    it "should be able to be gracefully rolled back" do
      expect(@pop).not_to be_received
      @pop.send{ raise Agent::Errors::Rollback }
      expect(@pop).not_to be_received
    end

    it "should continue when it was already closed" do
      @pop.close

      go!{ @pop.wait; @ack.send(Time.now) }

      sleep 0.2

      s, _ = @ack.receive

      expect(Time.now - s).to be_within(0.05).of(0.2)
    end
  end

  context "with a blocking_once" do
    before do
      @blocking_once = Agent::BlockingOnce.new
      @pop = Agent::Pop.new(:blocking_once => @blocking_once)
    end

    it "should only send only once" do
      expect(@blocking_once).not_to be_performed
      @pop.send{1}
      expect(@pop).to be_received
      expect(@blocking_once).to be_performed
      expect(@pop.object).to eq(1)

      @pop.send{2}
      expect(@pop.object).to eq(1)

      expect{@pop.send{raise "an error"} }.not_to raise_error
    end

    it "be able to be gracefully rolled back" do
      expect(@blocking_once).not_to be_performed
      expect(@pop).not_to be_received
      @pop.send{ raise Agent::Errors::Rollback }
      expect(@blocking_once).not_to be_performed
      expect(@pop).not_to be_received
    end

    it "should send only once even when it is closed" do
      @pop.close
      expect(@blocking_once).not_to be_performed
      @pop.send{1}
      expect(@pop).to be_received
      expect(@blocking_once).to be_performed
      expect(@pop.object).to be_nil

      @pop.send{2}
      expect(@pop.object).to be_nil

      expect{@pop.send{raise "an error"} }.not_to raise_error
    end
  end

  context "with a notifier" do
    before do
      @notifier = Agent::Notifier.new
      @pop = Agent::Pop.new(:notifier => @notifier)
    end

    it "should notify when being sent" do
      expect(@notifier).not_to be_notified
      @pop.send{1}
      expect(@notifier).to be_notified
    end

    it "should notify when being closed" do
      expect(@notifier).not_to be_notified
      @pop.close
      expect(@notifier).to be_notified
    end
  end

end
