# frozen_string_literal: true

require "spec_helper"
require "coach/middleware_validator"

describe Coach::MiddlewareValidator do
  subject(:validator) { described_class.new(head_middleware, already_provided) }

  let(:head_middleware) { build_middleware("Head") }
  let(:already_provided) { [] }

  let(:middleware_a) { build_middleware("A") }
  let(:middleware_b) { build_middleware("B") }
  let(:middleware_c) { build_middleware("C") }

  # head <── a
  #       └─ b <- c
  before do
    head_middleware.uses middleware_a
    head_middleware.uses middleware_b
    middleware_b.uses middleware_c
  end

  describe "#validated_provides!" do
    subject { -> { validator.validated_provides! } }

    context "with satisfied requires" do
      context "one level deep" do
        before do
          head_middleware.requires :a
          middleware_a.provides :a
        end

        it { is_expected.to_not raise_error }
      end

      context "that are inherited up" do
        before do
          head_middleware.requires :c
          middleware_c.provides :c
        end

        it { is_expected.to_not raise_error }
      end

      # Middlewares should be able to use the keys provided by the items `used` before
      # them. In this scenario, terminal will use a then b, and if b requires :a as a key
      # then our dependencies should be satisfied.
      context "that are inherited laterally" do
        before do
          middleware_a.provides :a
          middleware_b.requires :a
        end

        it { is_expected.to_not raise_error }
      end
    end

    context "with missing requirements" do
      context "at terminal" do
        before { head_middleware.requires :a, :c }

        it { is_expected.to raise_exception(/missing \[:a, :c\]/) }
      end

      context "from unordered middleware" do
        before do
          middleware_a.requires :b
          middleware_b.provides :b
        end

        it { is_expected.to raise_exception(/missing \[:b\]/) }
      end
    end
  end
end
