require 'spec_helper'

module Admino
  module Query
    describe Base do
      subject(:query) { Base.new(params, context, config) }
      let(:params) { {} }
      let(:context) { {} }
      let(:config) { nil }

      it 'takes a request params' do
        expect(query.params).to eq params
      end

      context 'with an explicit config' do
        let(:config) { Configuration.new }

        it 'uses it' do
          expect(query.config).to eq config
        end
      end

      context 'with a declared search_field' do
        let(:config) { Configuration.new }
        let(:search_field_config) { config.add_search_field(:search_field) }

        before do
          search_field_config
        end

        it 'returns a configured SearchField' do
          search_field = query.search_field_by_name(:search_field)
          expect(search_field.config).to eq search_field_config
          expect(search_field.params).to eq params
        end
      end

      context 'with a declared filter_group' do
        let(:config) { Configuration.new }
        let(:filter_group_config) { config.add_filter_group(:filter_group, [:one, :two]) }

        before do
          filter_group_config
        end

        it 'returns a configured FilterGroup' do
          filter_group = query.filter_group_by_name(:filter_group)
          expect(filter_group.config).to eq filter_group_config
          expect(filter_group.params).to eq params
          expect(filter_group.i18n_key).to eq :filter_group
        end
      end

      context 'with a declared sorting' do
        let(:config) { Configuration.new }
        let(:sorting_config) do
          config.add_sorting_scopes([:by_title, :by_date])
        end

        before do
          sorting_config
        end

        it 'returns a configured Sorting' do
          sorting = query.sorting
          expect(sorting.config).to eq sorting_config
          expect(sorting.params).to eq params
        end
      end

      describe '#scope' do
        let(:config) { Configuration.new }
        let(:result) { query.scope(starting_scope) }
        let(:starting_scope) { ScopeMock.new('explicit') }

        describe 'starting scope' do
          context 'with an explicit scope' do
            it 'uses it' do
              expect(result.name).to eq 'explicit'
            end
          end

          context 'with no explicit scope, but a default one configured' do
            let(:result) { query.scope }

            before do
              config.starting_scope_callable = Proc.new { |query|
                ScopeMock.new('configured').foo(query)
              }
            end

            before do
              result
            end

            it 'calls it with self and uses it' do
              expect(result.name).to eq 'configured'
              expect(result.chain).to eq [:foo, [query]]
            end
          end

          context 'with no scope' do
            let(:result) { query.scope }

            it 'raises a ArgumentError' do
              expect { result }.to raise_error(ArgumentError)
            end
          end
        end

        context 'with a set of search_fields, filter_groups and sortings' do
          let(:search_field_config) { config.add_search_field(:search_field) }
          let(:filter_group_config) { config.add_filter_group(:filter_group, [:one, :two]) }
          let(:sorting_config) { config.add_sorting_scopes([:title, :year]) }

          let(:params) do
            {
              query: {
                search_field: "foo",
                filter_group: "one",
              },
              sorting: "title",
              sort_order: "desc"
            }
          end

          before do
            search_field_config
            filter_group_config
            sorting_config
            query
          end

          context 'if query object does not respond to scopes' do
            it 'chains from starting scope' do
              expect(result.chain).to eq [
                :search_field, ["foo"],
                :one, [],
                :title, [:desc]
              ]
            end
          end

          context 'else' do
            let(:query_klass) do
              Class.new(Base) do
                def self.model_name
                  ActiveModel::Name.new(self, nil, "temp")
                end

                def search_field_scope(scope, foo)
                  scope.my_search_field(foo)
                end

                def one_scope(scope)
                  scope.my_one
                end

                def title_scope(scope, order)
                  scope.my_title(order)
                end
              end
            end

            subject(:query) do
              query_klass.new(params, context, config)
            end

            it 'chains from starting scope calling query object methods' do
              expect(result.chain).to eq [
                :my_search_field, ["foo"],
                :my_one, [],
                :my_title, [:desc]
              ]
            end
          end
        end

        context 'with a configured ending scope' do
          before do
            config.ending_scope_callable = Proc.new { |query| bar(query) }
          end

          it 'calls it with self at the end of the chain' do
            expect(result.chain).to eq [:bar, [query]]
          end
        end
      end
    end
  end
end

