require 'lib/executors/stack_executor'

class Devops::Executor::StackExecutor
  RSpec.describe self, type: :executor, stubbed_logger: true do
    let(:out) { double('out', puts: nil, flush: nil) }
    let(:stack) { build(:stack) }
    let(:executor_without_stack) { described_class.new(out: out, current_user: 'user') }
    let(:executor_with_stack) { described_class.new(out: out, stack: stack, current_user: 'user') }
    let(:new_servers_by_priorities) {
      {
        0 => [{id: 1}],
        2 => [{id: 2}]
      }
    }
    let(:just_persisted_by_priority) {
      {
        0 => [double('info 1', id: 1)],
        2 => [double('info 2', id: 2)]
      }
    }
    let(:fetcher) {
      instance_double(StackServersFetcher,
        new_servers_by_priorities: new_servers_by_priorities,
        stale_servers: build_list(:server, 2),
        fetch: nil
      )
    }

    before do
      allow(executor_with_stack).to receive(:fetcher) { fetcher }
    end


    describe '#wait_till_stack_is_created' do
      let(:waiter) { instance_double(StackCreationWaiter, wait: double("creation_result", ok?: true)) }

      before do
        allow(executor_with_stack).to receive(:waiter) { waiter }
        allow(stack).to receive(:unlock_persisting!)
      end

      it 'waites till stack is created, then fetches stack servers and unlocks stack persisting' do
        expect(waiter).to receive(:wait).ordered
        expect(stack).to receive(:unlock_persisting!).ordered
        executor_with_stack.wait_till_stack_is_created
      end

      it "return true if syncer returns ok" do
        expect(executor_with_stack.wait_till_stack_is_created).to be true
      end

      it "return false if syncer returns not ok" do
        allow(waiter).to receive(:wait) { double("creation_result", ok?: false, reason: '') }
        expect(executor_with_stack.wait_till_stack_is_created).to be false
      end
    end

    describe '#create_stack' do
      let(:created_stack) { instance_double(Devops::Model::StackAws, save: nil) }

      before do
        allow(Devops::Model::StackFactory).to receive(:create).and_return(created_stack)
      end

      it 'initiate creation in cloud and persists stack' do
        expect(Devops::Model::StackFactory).to receive(:create).with('aws', instance_of(Hash), out)
        expect(created_stack).to receive(:save)
        executor_with_stack.create_stack({'provider' => 'aws'})
      end

      it 'locks persisting on create' do
        executor_with_stack.create_stack({'provider' => 'aws'})
        expect(Devops::Model::StackFactory).to have_received(:create) do |_, params|
          expect(params).to include('persisting_is_locked' => true)
        end
      end
    end

    describe '#persist_new_servers' do
      let(:persister) { instance_double(StackServersPersister, persist: build(:server)) }

      before do
        allow(executor_with_stack).to receive(:persister) { persister }
        allow(stack).to receive(:lock_persisting!)
        allow(stack).to receive(:unlock_persisting!)
        allow(executor_with_stack).to receive(:reload_stack) { stack }
      end

      it 'calls StackServersPersister#persist for each server' do
        expect(persister).to receive(:persist).exactly(2).times
        executor_with_stack.persist_new_servers
      end

      it 'locks persisting of a stack before start and unlocks after finish' do
        expect(stack).to receive(:lock_persisting!).ordered
        expect(persister).to receive(:persist).ordered
        expect(stack).to receive(:unlock_persisting!).ordered
        executor_with_stack.persist_new_servers
      end

      it 'unlocks persisting even in case of failures' do
        allow(persister).to receive(:persist) { raise }
        expect(stack).to receive(:unlock_persisting!)
        expect { executor_with_stack.persist_new_servers }.to raise_error StandardError
      end
    end

    describe '#bootstrap_just_persisted' do
      it 'calls PrioritizedGroupsBootstrapper#bootstrap_servers_by_priority' do
        result = double('bootstrap_result')
        allow(executor_with_stack).to receive(:just_persisted_by_priority) { just_persisted_by_priority }
        allow_any_instance_of(PrioritizedGroupsBootstrapper).to receive(:bootstrap_servers_by_priority) { result }
        expect_any_instance_of(PrioritizedGroupsBootstrapper).to receive(:bootstrap_servers_by_priority)
        expect(executor_with_stack.bootstrap_just_persisted(1000)).to eq result
      end
    end

    describe '#delete_stale_servers' do
      it 'builds server executor per stale server and properly delete them' do
        executor1 = instance_double(Devops::Executor::ServerExecutor, delete_server: nil)
        executor2 = instance_double(Devops::Executor::ServerExecutor, delete_server: nil)
        allow(Devops::Executor::ServerExecutor).to receive(:new).and_return(executor1, executor2)
        expect(executor1).to receive(:delete_server).ordered
        expect(executor2).to receive(:delete_server).ordered
        executor_with_stack.delete_stale_servers
      end
    end

    describe '#delete_stack' do
      it 'deletes stack from cloud, then deletes stack servers, and then deletes stack itself' do
        allow(stack).to receive(:delete)
        expect(stack).to receive(:delete_stack_in_cloud!).ordered
        expect(Devops::Model::Server).to receive(:delete_stack_servers).ordered
        expect(stack).to receive(:delete).ordered
        executor_with_stack.delete_stack
      end
    end
  end
end