require 'workers/stack_bootstrap_worker'
require 'lib/executors/stack_executor'

RSpec.describe StackBootstrapWorker, type: :worker, stubbed_connector: true, init_messages: true do
  let(:stack_attrs) { attributes_for(:stack_aws).stringify_keys }
  let(:worker) { described_class.new }
  let(:perform_with_bootstrap) { worker.perform('stack_attributes' => stack_attrs) }
  let(:perform_without_bootstrap) { worker.perform('stack_attributes' => stack_attrs, 'without_bootstrap' => true) }
  let(:executor) {
    instance_double(Devops::Executor::StackExecutor,
      wait_till_stack_is_created: true,
      create_stack: Devops::Model::StackAws.new(stack_attrs),
      persist_new_servers: nil,
      delete_stack: nil,
      bootstrap_just_persisted: bootstrap_result(:ok)
    )
  }

  def bootstrap_result(reason)
    Devops::Executor::StackExecutor::ServersBootstrapper::Result.from_reason(reason)
  end

  before do
    allow(worker).to receive(:set_task_data)
    allow(worker).to receive(:executor) { executor }
    allow(worker).to receive(:call).and_yield
  end


  it 'requires "stack_attributes" in options' do
    expect{ worker.perform({}) }.to raise_error KeyError
  end

  it 'updates report about operation' do
    expect(worker).to receive(:set_task_data)
    perform_without_bootstrap
  end

  it 'updates report about operation, creates stack and persists stack servers' do
    expect(worker).to receive(:set_task_data).ordered
    expect(executor).to receive(:create_stack).ordered
    expect(executor).to receive(:persist_new_servers).ordered
    perform_without_bootstrap
  end

  it "waits for stack creation to be completed" do
    expect(executor).to receive(:wait_till_stack_is_created)
    perform_with_bootstrap
  end

  it "returns 1 if waiting wasn't successful" do
    allow(executor).to receive(:wait_till_stack_is_created) { false }
    expect(perform_with_bootstrap).to eq 1
  end

  context 'if without_bootstrap is true' do
    it "doesn't bootstrap servers" do
      expect(worker).not_to receive(:bootstrap_or_rollback_if_failed)
      perform_without_bootstrap
    end

    it 'returns 0' do
      expect(perform_without_bootstrap).to eq 0
    end
  end

  context 'if without_bootstrap is false or not set' do
    it 'returns 0 when bootstraping servers was successful' do
      expect(perform_with_bootstrap).to eq 0
    end

    it 'rollbacks stack and returns 2 when a known error occured during servers bootstrap' do
      allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:bootstrap_error) }
      expect(executor).to receive(:delete_stack)
      perform_with_bootstrap
      expect(perform_with_bootstrap).to eq 2
    end

    it "doesn't rollback stack and returns 3 when a known error occured during servers deploy" do
      allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:deploy_error) }
      expect(worker).not_to receive(:rollback_stack!)
      expect(perform_with_bootstrap).to eq 3
    end

    it "doesn't rollback stack and returns 3 when a servers bootstrap & deploy haven't been finished due to timeout" do
      allow(executor).to receive(:bootstrap_just_persisted) { bootstrap_result(:timeout_reached) }
      expect(worker).not_to receive(:rollback_stack!)
      expect(perform_with_bootstrap).to eq 4
    end

    it 'rollbacks stack and reraises that error when an unknown error occured during servers bootsrap and deploy' do
      error = StandardError.new
      allow(executor).to receive(:bootstrap_just_persisted) { raise error }
      expect(worker).to receive(:rollback_stack!)
      expect{perform_with_bootstrap}.to raise_error(error)
    end
  end
end
