require 'rails_helper'

describe DryRunnable do
  class Agents::SandboxedAgent < Agent
    default_schedule "3pm"

    can_dry_run!

    def check
      perform
    end

    def receive(events)
      events.each do |event|
        perform(event.payload['prefix'])
      end
    end

    private

    def perform(prefix = nil)
      log "Logging"
      create_event payload: { 'test' => "#{prefix}foo" }
      error "Recording error"
      create_event payload: { 'test' => "#{prefix}bar" }
      self.memory = { 'last_status' => 'ok', 'dry_run' => dry_run? }
      save!
    end
  end

  before do
    stub(Agents::SandboxedAgent).valid_type?("Agents::SandboxedAgent") { true }

    @agent = Agents::SandboxedAgent.create(name: "some agent") { |agent|
      agent.user = users(:bob)
    }
  end

  def counts
    [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count]
  end

  it "does not affect normal run, with dry_run? returning false" do
    before = counts
    after  = before.zip([0, 2, 2]).map { |x, d| x + d }

    expect {
      @agent.check
      @agent.reload
    }.to change { counts }.from(before).to(after)

    expect(@agent.memory).to eq({ 'last_status' => 'ok', 'dry_run' => false })

    payloads = @agent.events.reorder(:id).last(2).map(&:payload)
    expect(payloads).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }])

    messages = @agent.logs.reorder(:id).last(2).map(&:message)
    expect(messages).to eq(['Logging', 'Recording error'])
  end

  it "does not perform dry-run if Agent does not support dry-run" do
    stub(@agent).can_dry_run? { false }

    results = nil

    expect {
      results = @agent.dry_run!
      @agent.reload
    }.not_to change {
      [@agent.memory, counts]
    }

    expect(results[:log]).to match(/\A\[\d\d:\d\d:\d\d\] INFO -- : Dry Run failed\n\[\d\d:\d\d:\d\d\] ERROR -- : Exception during dry-run. SandboxedAgent does not support dry-run: /)
    expect(results[:events]).to eq([])
    expect(results[:memory]).to eq({})
  end

  describe "dry_run!" do
    it "traps any destructive operations during a run" do
      results = nil

      expect {
        results = @agent.dry_run!
        @agent.reload
      }.not_to change {
        [@agent.memory, counts]
      }

      expect(results[:log]).to match(/\A\[\d\d:\d\d:\d\d\] INFO -- : Dry Run started\n\[\d\d:\d\d:\d\d\] INFO -- : Logging\n\[\d\d:\d\d:\d\d\] ERROR -- : Recording error\n/)
      expect(results[:events]).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }])
      expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true })
    end

    it "traps any destructive operations during a run when an event is given" do
      results = nil

      expect {
        results = @agent.dry_run!(Event.new(payload: { 'prefix' => 'super' }))
        @agent.reload
      }.not_to change {
        [@agent.memory, counts]
      }

      expect(results[:log]).to match(/\A\[\d\d:\d\d:\d\d\] INFO -- : Dry Run started\n\[\d\d:\d\d:\d\d\] INFO -- : Logging\n\[\d\d:\d\d:\d\d\] ERROR -- : Recording error\n/)
      expect(results[:events]).to eq([{ 'test' => 'superfoo' }, { 'test' => 'superbar' }])
      expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true })
    end
  end
end