RSpec.describe Aldous::Controller::ActionExecutionService do
  let(:action_execution_service) {described_class.new(controller, controller_action_class)}

  let(:controller) {double "controller", head: nil, performed?: false}
  let(:controller_action_class) {double "controller_action_class", build: action}

  let(:action) {double 'action', perform: action_result, default_view_data: default_view_data}
  let(:action_result) {'foobar'}
  let(:default_view_data) {'hello'}

  describe "::perform" do
    subject(:perform) {described_class.perform(controller, controller_action_class)}

    let(:action_execution_service) {double "action_execution_service", perform: nil}

    before do
      allow(described_class).to receive(:new).and_return(action_execution_service)
    end

    it "instantiates the action execution service" do
      expect(described_class).to receive(:new).with(controller, controller_action_class)
      perform
    end

    it "performs the action execution service" do
      expect(action_execution_service).to receive(:perform)
      perform
    end
  end

  describe "#perform" do
    subject(:perform) {action_execution_service.perform}

    let(:preconditions_execution_service) {double "preconditions_execution_service", perform: preconditions_result}
    let(:preconditions_result) {[nil, nil]}

    before do
      allow(Aldous::Controller::PreconditionsExecutionService).to receive(:new).and_return(preconditions_execution_service)
      allow(Aldous::Controller::Action::ResultExecutionService).to receive(:perform)
    end

    context "when preconditions did not halt execution" do
      it "executes the result with action_result" do
        expect(Aldous::Controller::Action::ResultExecutionService).to receive(:perform).with(controller, action_result, default_view_data)
        perform
      end

      context "but action perform halted execution due to render" do
        before do
          allow(controller).to receive(:performed?).and_return(false, true)
        end

        it "returns nil" do
          expect(perform).to be_nil
        end

        it "does not execute the result" do
          expect(Aldous::Controller::Action::ResultExecutionService).to_not receive(:perform)
          perform
        end
      end
    end

    context "when preconditions did halt execution" do
      let(:preconditions_result) {[precondition, precondition_result]}

      let(:precondition) {double 'precondition', perform: precondition_result, default_view_data: default_view_data}
      let(:precondition_result) {'preconditionfoobar'}
      let(:default_view_data) {'precondtionhello'}

      it "executes the result with precondition_result" do
        expect(Aldous::Controller::Action::ResultExecutionService).to receive(:perform).with(controller, precondition_result, default_view_data)
        perform
      end

      context "via a render on the controller object" do
        let(:controller) {double "controller", head: nil, performed?: true}

        it "returns nil" do
          expect(perform).to be_nil
        end

        it "does not execute the result" do
          expect(Aldous::Controller::Action::ResultExecutionService).to_not receive(:perform)
          perform
        end
      end
    end

    context "when error occurs" do
      before do
        allow(Aldous::LoggingWrapper).to receive(:log)
        allow(Aldous::Controller::PreconditionsExecutionService).to receive(:new).and_raise
      end

      it "logs the error" do
        expect(Aldous::LoggingWrapper).to receive(:log)
        perform
      end

      it "returns a 500 via controller" do
        expect(controller).to receive(:head).with(:internal_server_error)
        perform
      end
    end
  end
end
