require 'spec_helper'

describe IndexSweeper do

  describe "#async_cleanup" do
    it "should index items that were expected but not found" do
      expect(AsyncIndexer).to receive(:index).with(Work, [2], "cleanup")

      IndexSweeper.async_cleanup(Work, [1,2], [1])
    end
  end

  it "should record the first failure with a count of 1, and retry" do
    sweeper = IndexSweeper.new(batch, WorkIndexer)
    indexer = AsyncIndexer.new(WorkIndexer, "failures")

    expect(AsyncIndexer).to receive(:new).with(WorkIndexer, "failures").and_return(indexer)
    expect(indexer).to receive(:enqueue_ids).with([53])

    sweeper.process_batch

    expect(
      JSON.parse(IndexSweeper::REDIS.hget("WorkIndexer:failures", 53))
    ).to include("error" => { "an error" => "with a message" }, "count" => 1)
  end

  it "record the second failure with a count of 2, and retry" do
    sweeper = IndexSweeper.new(batch, WorkIndexer)
    indexer = AsyncIndexer.new(WorkIndexer, "failures")

    IndexSweeper::REDIS.hset(
      "WorkIndexer:failures",
      53,
      [{ "error" => { "an error" => "with a message" }, "count" => 1 }].to_json
    )

    expect(AsyncIndexer).to receive(:new).with(WorkIndexer, "failures").and_return(indexer)
    expect(indexer).to receive(:enqueue_ids).with([53])

    sweeper.process_batch

    expect(
      JSON.parse(IndexSweeper::REDIS.hget("WorkIndexer:failures", 53))
    ).to include("error" => { "an error" => "with a message" }, "count" => 2)
  end

  it "should record the third failure with a count of 3, but not retry" do
    sweeper = IndexSweeper.new(batch, WorkIndexer)

    IndexSweeper::REDIS.hset(
      "WorkIndexer:failures",
      53,
      [{ "error" => { "an error" => "with a message" }, "count" => 2 }].to_json
    )

    expect(AsyncIndexer).not_to receive(:new).with(WorkIndexer, "failures")

    sweeper.process_batch

    expect(
      JSON.parse(IndexSweeper::REDIS.hget("WorkIndexer:failures", 53))
    ).to include("error" => { "an error" => "with a message" }, "count" => 3)

    # In addition to having a count of 3, it should show up on the list of
    # permanent failures.
    expect(
      IndexSweeper.permanent_failures(WorkIndexer)
    ).to include("53" => { "an error" => "with a message" })
  end

  context "when documents succeed" do
    let(:batch) {
      {
        "errors" => false,
        "items" => [
          { "update" => { "_id" => "11" } },
          { "delete" => { "_id" => "12" } },
          { "index" => { "_id" => "13" } }
        ]
      }
    }

    let(:sweeper) { IndexSweeper.new(batch, WorkIndexer) }

    it "should remove documents from stores if they succeed" do
      IndexSweeper::REDIS.hset(
        "WorkIndexer:failures",
        11,
        [{ "error" => { "an error" => "a message" }, "count" => 1 }].to_json
      )

      IndexSweeper::REDIS.hset(
        "WorkIndexer:failures:second",
        12,
        [{ "error" => { "an error" => "a message" }, "count" => 2 }].to_json
      )

      IndexSweeper::REDIS.hset(
        "WorkIndexer:failures:permanent",
        13,
        [{ "error" => { "an error" => "a message" }, "count" => 3 }].to_json
      )

      expect(AsyncIndexer).not_to receive(:new)

      sweeper.process_batch

      # There should be no recorded errors:
      expect(IndexSweeper::REDIS.hvals("WorkIndexer:failures")).to eq([])
    end

    it "should perform success callbacks with the successful ids" do
      expect(WorkIndexer).to receive(:handle_success).with(%w[11 12 13])
                                                     .and_call_original
      expect(Work).to receive(:successful_reindex).with(%w[11 12 13])
      sweeper.process_batch
    end
  end

  it "should grab the elasticsearch ids expected by the indexer for retries" do
    work = create(:work)
    work.stat_counter.update!(id: 3)

    sweeper = IndexSweeper.new(batch(work.id), StatCounterIndexer)
    indexer = AsyncIndexer.new(StatCounterIndexer, "failures")

    expect(AsyncIndexer).to receive(:new).with(StatCounterIndexer, "failures").at_least(:once).and_return(indexer)
    expect(indexer).to receive(:enqueue_ids).with([work.stat_counter.id]).at_least(:once).and_call_original

    sweeper.process_batch
  end

  it "doesn't trigger an error if the batch results are empty" do
    sweeper = IndexSweeper.new(nil, WorkIndexer)
    expect { sweeper.process_batch }.not_to raise_exception
  end

  private

  def batch(id = 53)
    {
      "errors" => true,
      "items" => [
        {
          "update" => {
            "_id" => id,
            "error" => {
              "an error" => "with a message"
            }
          }
        }
      ]
    }
  end

end
