require 'spec_helper'

# rubocop:disable Metrics/BlockLength
describe GrapePathHelpers::NamedRouteMatcher do
  include described_class

  let(:helper_class) do
    fake_class = Class.new do
      prepend GrapePathHelpers::NamedRouteMatcher

      def method_missing(method_id, *args, **kwargs)
        [method_id, args, kwargs] || super
      end

      def respond_to_missing?(_method_name, _include_private = false)
        super
      end
    end

    fake_class.new
  end

  describe '#method_missing' do
    it 'returns super method_missing if the route does not exist' do
      expect(helper_class.test_method(id: 1))
        .to eq([:test_method, [], { id: 1 }])
    end

    it 'returns super method_missing if first arg is not a hash' do
      expect(helper_class.api_v1_cats_path(:arg1, kwarg1: :kwarg1))
        .to eq([:api_v1_cats_path, [:arg1], { kwarg1: :kwarg1 }])
    end

    context 'when method name matches a Grape::Route path helper name' do
      it 'returns the path for that route object' do
        expect(helper_class.api_v1_ping_path).to eq('/api/v1/ping.json')
      end

      context 'when route contains dynamic segments' do
        it 'returns the path for that route object' do
          expect(helper_class.api_v1_cats_path(id: 5))
            .to eq('/api/v1/cats/5.json')
        end
      end

      context 'when route requires dynamic segments but none are passed in' do
        it 'returns super method_missing' do
          expect(helper_class.api_v1_cats_owners_path)
            .to eq([:api_v1_cats_owners_path, [], {}])
        end
      end

      context 'when route has no dynamic segments but some are passed in' do
        it 'returns super method_missing' do
          expect(helper_class.api_v1_ping_path(invalid: 'test'))
            .to eq([:api_v1_ping_path, [], { invalid: 'test' }])
        end
      end
    end
  end

  describe '#respond_to_missing?' do
    context 'when method name doesnt end with _path suffix' do
      let(:method_name) { :api_v1_cats }

      it 'returns false' do
        expect(respond_to_missing?(method_name)).to eq(false)
      end

      it 'doesnt execute decorated_routes_by_helper_name method' do
        expect(Grape::API::Instance)
          .not_to receive(:decorated_routes_by_helper_name)

        respond_to_missing?(method_name)
      end
    end

    context 'when method name matches a Grape::Route path with segments' do
      let(:method_name) { :api_v1_cats_path }

      it 'returns true' do
        expect(respond_to_missing?(method_name)).to eq(true)
      end
    end

    context 'when method name matches a Grape::Route path' do
      let(:method_name) { :api_v1_ping_path }

      it 'returns true' do
        expect(respond_to_missing?(method_name)).to eq(true)
      end
    end

    context 'when method name does not match a Grape::Route path' do
      let(:method_name) { :some_other_path }

      it 'returns false' do
        expect(respond_to_missing?(method_name)).to eq(false)
      end
    end
  end

  context 'when Grape::Route objects share the same helper name' do
    context 'when helpers require different segments to generate their path' do
      it 'uses arguments to infer which route to use' do
        show_path = helper_class.api_v1_cats_path(
          'id' => 1
        )
        expect(show_path).to eq('/api/v1/cats/1.json')

        index_path = helper_class.api_v1_cats_path
        expect(index_path).to eq('/api/v1/cats.json')
      end

      it 'does not get shadowed by another route with less segments' do
        show_path = helper_class.api_v1_cats_owners_path(
          'id' => 1
        )
        expect(show_path).to eq('/api/v1/cats/1/owners.json')

        show_path = helper_class.api_v1_cats_owners_path(
          'id' => 1,
          'owner_id' => 1
        )
        expect(show_path).to eq('/api/v1/cats/1/owners/1.json')
      end
    end

    context 'when query params are passed in' do
      it 'uses arguments to infer which route to use' do
        show_path = helper_class.api_v1_cats_path(
          'id' => 1,
          params: { 'foo' => 'bar' }
        )

        expect(show_path).to eq('/api/v1/cats/1.json?foo=bar')

        index_path = helper_class.api_v1_cats_path(
          params: { 'foo' => 'bar' }
        )
        expect(index_path).to eq('/api/v1/cats.json?foo=bar')
      end
    end
  end
end
# rubocop:enable Metrics/BlockLength
