require 'spec_helper'

describe Mongo::Cluster::CursorReaper do

  after do
    authorized_collection.delete_many
  end

  let(:reaper) do
    described_class.new
  end

  let(:active_cursors) do
    reaper.instance_variable_get(:@active_cursors)
  end

  describe '#intialize' do

    it 'initializes a hash for servers and their kill cursors ops' do
      expect(reaper.instance_variable_get(:@to_kill)).to be_a(Hash)
    end

    it 'initializes a set for the list of active cursors' do
      expect(reaper.instance_variable_get(:@active_cursors)).to be_a(Set)
    end
  end

  describe '#schedule_kill_cursor' do

    let(:server) { double('server') }
    let(:cursor_id) { 1 }
    let(:op_spec_1) { double('op_spec_1') }
    let(:op_spec_2) { double('op_spec_2') }
    let(:to_kill) { reaper.instance_variable_get(:@to_kill)}

    context 'when the cursor is on the list of active cursors' do

      before do
        reaper.register_cursor(cursor_id)
      end

      context 'when there is not a list already for the server' do

        before do
          reaper.schedule_kill_cursor(cursor_id, op_spec_1, server)
        end

        it 'initializes the list of op specs to a set' do
          expect(to_kill.keys).to eq([ server ])
          expect(to_kill[server]).to eq(Set.new([op_spec_1]))
        end
      end

      context 'when there is a list of ops already for the server' do

        before do
          reaper.schedule_kill_cursor(cursor_id, op_spec_1, server)
          reaper.schedule_kill_cursor(cursor_id, op_spec_2, server)
        end

        it 'adds the op to the server list' do
          expect(to_kill.keys).to eq([ server ])
          expect(to_kill[server]).to contain_exactly(op_spec_1, op_spec_2)
        end

        context 'when the same op is added more than once' do

          before do
            reaper.schedule_kill_cursor(cursor_id, op_spec_2, server)
          end

          it 'does not allow duplicates ops for a server' do
            expect(to_kill.keys).to eq([ server ])
            expect(to_kill[server]).to contain_exactly(op_spec_1, op_spec_2)
          end
        end
      end
    end

    context 'when the cursor is not on the list of active cursors' do

      before do
        reaper.schedule_kill_cursor(cursor_id, op_spec_1, server)
      end

      it 'does not add the kill cursors op spec to the list' do
        expect(to_kill).to eq({})
      end
    end
  end

  describe '#register_cursor' do

    before do
      reaper.register_cursor(cursor_id)
    end

    context 'when the cursor id is nil' do

      let(:cursor_id) do
        nil
      end

      it 'does not register the cursor' do
        expect(active_cursors.size).to be(0)
      end
    end

    context 'when the cursor id is 0' do

      let(:cursor_id) do
        0
      end

      it 'does not register the cursor' do
        expect(active_cursors.size).to be(0)
      end
    end

    context 'when the cursor id is a valid id' do

      let(:cursor_id) do
        2
      end

      it 'registers the cursor id as active' do
        expect(active_cursors).to eq(Set.new([2]))
      end
    end
  end

  describe '#unregister_cursor' do

    context 'when the cursor id is in the active cursors list' do

      before do
        reaper.register_cursor(2)
        reaper.unregister_cursor(2)
      end

      it 'removes the cursor id' do
        expect(active_cursors.size).to eq(0)
      end
    end
  end
end
