# frozen_string_literal: true require "spec_helper" require "coach/cli/provider_finder" describe Coach::Cli::ProviderFinder do subject(:provider_finder) { described_class.new(middleware_name, value_name) } let(:middleware_name) { "" } let(:value_name) { "" } describe "#find_provider" do context "when there is a single provider" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses ProvidingMiddleware requires :provided_value end) end it "returns the providing middleware" do expect(provider_finder.find_provider).to eq %w[ProvidingMiddleware] end end context "when there are multiple providers" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("FirstProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const("SecondProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses FirstProvidingMiddleware uses SecondProvidingMiddleware requires :provided_value end) end it "returns the providing middleware" do expect(provider_finder.find_provider). to eq %w[FirstProvidingMiddleware SecondProvidingMiddleware] end end context "when there's an intermediate middleware after the provider" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const("IntermediateMiddleware", Class.new(Coach::Middleware) do uses ProvidingMiddleware end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses IntermediateMiddleware requires :provided_value end) end it "returns the providing middleware" do expect(provider_finder.find_provider).to eq %w[ProvidingMiddleware] end end context "when the middleware can't be found" do let(:middleware_name) { "MiddlewareThatDoesntExist" } it "raises a MiddlewareNotFoundError" do expect { provider_finder.find_provider }. to raise_error(Coach::Cli::Errors::MiddlewareNotFoundError) end end context "when the middleware doesn't require the specified value" do let(:middleware_name) { "MiddlewareWithoutRequires" } let(:value_name) { "value_that_doesnt_exist" } before do stub_const(middleware_name, Class.new(Coach::Middleware)) end it "raises a ValueNotRequiredError" do expect { provider_finder.find_provider }. to raise_error(Coach::Cli::Errors::ValueNotRequiredError) end end context "when the middleware isn't provided with a value it requires" do let(:middleware_name) { "MiddlewareWithMissingProvide" } let(:value_name) { "value_that_isnt_provided" } before do stub_const(middleware_name, Class.new(Coach::Middleware) do requires :value_that_isnt_provided end) end it "raises a ValueNotProvidedError" do expect { provider_finder.find_provider }. to raise_error(Coach::Cli::Errors::ValueNotProvidedError) end end end describe "#find_chain" do context "when there is a single provider" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses ProvidingMiddleware requires :provided_value end) end it "returns the providing middleware" do expect(provider_finder.find_chain). to eq [%w[ProvidingMiddleware RequiringMiddleware]].to_set end end context "when there are multiple paths to a single provider" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("ProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const("FirstIntermediateMiddleware", Class.new(Coach::Middleware) do uses ProvidingMiddleware end) stub_const("SecondIntermediateMiddleware", Class.new(Coach::Middleware) do uses ProvidingMiddleware end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses FirstIntermediateMiddleware uses SecondIntermediateMiddleware requires :provided_value end) end it "returns multiple middleware chains" do expect(provider_finder.find_chain). to eq [ %w[ProvidingMiddleware FirstIntermediateMiddleware RequiringMiddleware], %w[ProvidingMiddleware SecondIntermediateMiddleware RequiringMiddleware], ].to_set end end context "when there are multiple paths to multiple providers" do let(:middleware_name) { "RequiringMiddleware" } let(:value_name) { "provided_value" } before do stub_const("FirstProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const("SecondProvidingMiddleware", Class.new(Coach::Middleware) do provides :provided_value end) stub_const("FirstIntermediateMiddleware", Class.new(Coach::Middleware) do uses FirstProvidingMiddleware end) stub_const("SecondIntermediateMiddleware", Class.new(Coach::Middleware) do uses SecondProvidingMiddleware end) stub_const(middleware_name, Class.new(Coach::Middleware) do uses FirstIntermediateMiddleware uses SecondIntermediateMiddleware requires :provided_value end) end it "returns multiple middleware chains" do expect(provider_finder.find_chain). to eq([ %w[FirstProvidingMiddleware FirstIntermediateMiddleware RequiringMiddleware], %w[SecondProvidingMiddleware SecondIntermediateMiddleware RequiringMiddleware], # rubocop:disable Metrics/LineLength ].to_set) end end context "when the middleware can't be found" do let(:middleware_name) { "MiddlewareThatDoesntExist" } it "raises a MiddlewareNotFoundError" do expect { provider_finder.find_chain }. to raise_error(Coach::Cli::Errors::MiddlewareNotFoundError) end end context "when the middleware doesn't require the specified value" do let(:middleware_name) { "MiddlewareWithoutRequires" } let(:value_name) { "value_that_doesnt_exist" } before do stub_const(middleware_name, Class.new(Coach::Middleware)) end it "raises a ValueNotRequiredError" do expect { provider_finder.find_chain }. to raise_error(Coach::Cli::Errors::ValueNotRequiredError) end end context "when the middleware isn't provided with a value it requires" do let(:middleware_name) { "MiddlewareWithMissingProvide" } let(:value_name) { "value_that_isnt_provided" } before do stub_const(middleware_name, Class.new(Coach::Middleware) do requires :value_that_isnt_provided end) end it "raises a ValueNotProvidedError" do expect { provider_finder.find_chain }. to raise_error(Coach::Cli::Errors::ValueNotProvidedError) end end end end