# frozen_string_literal: true

RSpec.describe Dry::Transaction::StepAdapters::Try do
  subject { described_class.new }

  let(:operation) {
    -> (input) {
      raise(Test::NotValidError, "not a string") unless input.is_a? String

      input.upcase
    }
  }

  let(:options) { {catch: Test::NotValidError, step_name: "unit"} }

  before do
    Test::NotValidError = Class.new(StandardError)
    Test::BetterNamingError = Class.new(StandardError)
  end

  describe "#call" do
    context "without the :catch option" do
      let(:options) { {step_name: "unit"} }

      it "raises an ArgumentError" do
        expect {
          subject.(operation, options, ["something"])
        }.to raise_error(
          Dry::Transaction::MissingCatchListError,
          "step +unit+ requires one or more exception classes provided via +catch:+"
        )
      end
    end

    context "with the :catch option" do
      context "when the error was raised" do
        it "returns a Failure value" do
          expect(subject.(operation, options, [1234])).to be_a_failure
        end

        it "returns the raised error as output" do
          result = subject.(operation, options, [1234])
          expect(result.failure).to be_a Test::NotValidError
          expect(result.failure.message).to eql("not a string")
        end

        context "when using the :raise option" do
          let(:options) {
            {
              catch: Test::NotValidError,
              raise: Test::BetterNamingError
            }
          }

          it "returns a Failure value" do
            expect(subject.(operation, options, [1234])).to be_a_failure
          end

          it "returns the error specified by :raise as output" do
            result = subject.(operation, options, [1234])
            expect(result.failure).to be_a Test::BetterNamingError
            expect(result.failure.message).to eql("not a string")
          end
        end
      end

      context "when the error was NOT raised" do
        it "returns a Success value" do
          expect(subject.(operation, options, ["input"])).to eql(Success("INPUT"))
        end

        context "when using the :raise option" do
          let(:options) {
            {
              catch: Test::NotValidError,
              raise: Test::BetterNamingError
            }
          }

          it "returns a Success value" do
            expect(subject.(operation, options, ["input"])).to eql(Success("INPUT"))
          end
        end
      end
    end
  end
end
