RSpec.describe SagaRunner do
  subject(:runner) { SagaRunner.new id }

  let(:id) { SecureRandom.uuid }

  let(:causation_id) { SecureRandom.uuid }
  let(:correlation_id) { SecureRandom.uuid }
  let(:saga) { spy causation_id: causation_id, correlation_id: correlation_id }
  let(:handler) { -> { @ran = true } }
  let(:saga_class) do
    double new: saga, initial: :first_step, handler_for_step: handler
  end

  before do
    stub_const 'TestSaga', saga_class

    config = double(server_node?: true, node_name: SecureRandom.uuid, nodes: [])
    allow(Aggro).to receive(:cluster_config).and_return config
  end

  describe '#apply_command' do
    context 'the command is a StartSaga' do
      let(:details) { { test: 'foo' } }
      let(:command) do
        SagaRunner::StartSaga.new name: 'TestSaga', id: id, details: details
      end

      it 'should create a new saga based on the given name' do
        runner.send :apply_command, command
        expect(saga_class).to have_received(:new)
      end

      it 'should set @runner on the saga' do
        runner.send :apply_command, command
        expect(saga.instance_variable_get(:@runner)).to eq runner
      end

      it 'should execute the initial step' do
        runner.send :apply_command, command
        expect(saga.instance_variable_get(:@ran)).to be_truthy
      end
    end
  end

  describe '#reject' do
    let(:proxy) { spy }

    before do
      allow(runner).to receive(:did).and_return proxy
      runner.instance_variable_set :@saga, saga
    end

    it 'should call the rejected event' do
      runner.reject 'reason'

      expect(proxy).to have_received(:rejected).with reason: 'reason'
    end
  end

  describe '#resolve' do
    let(:proxy) { spy }

    before do
      allow(runner).to receive(:did).and_return proxy
      runner.instance_variable_set :@saga, saga
    end

    it 'should call the resolved event' do
      runner.resolve 'value'

      expect(proxy).to have_received(:resolved).with value: 'value'
    end
  end

  describe '#did' do
    it 'should set the @_context with @details' do
      runner.instance_variable_set :@details,  foo: 'bar'

      runner.send :did

      expect(runner.instance_variable_get(:@_context)[:foo]).to eq 'bar'
    end
  end
end
