require 'spec_helper'

describe Grape::Validations do
  context 'using a custom length validator' do
    before do
      module CustomValidationsSpec
        class DefaultLength < Grape::Validations::Base
          def validate_param!(attr_name, params)
            @option = params[:max].to_i if params.key?(:max)
            return if params[attr_name].length <= @option
            raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
          end
        end
      end
    end
    subject do
      Class.new(Grape::API) do
        params do
          requires :text, default_length: 140
        end
        get do
          'bacon'
        end
      end
    end

    def app
      subject
    end

    it 'under 140 characters' do
      get '/', text: 'abc'
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end
    it 'over 140 characters' do
      get '/', text: 'a' * 141
      expect(last_response.status).to eq 400
      expect(last_response.body).to eq 'text must be at the most 140 characters long'
    end
    it 'specified in the query string' do
      get '/', text: 'a' * 141, max: 141
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end
  end

  context 'using a custom body-only validator' do
    before do
      module CustomValidationsSpec
        class InBody < Grape::Validations::PresenceValidator
          def validate(request)
            validate!(request.env['api.request.body'])
          end
        end
      end
    end
    subject do
      Class.new(Grape::API) do
        params do
          requires :text, in_body: true
        end
        get do
          'bacon'
        end
      end
    end

    def app
      subject
    end

    it 'allows field in body' do
      get '/', text: 'abc'
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end
    it 'ignores field in query' do
      get '/', nil, text: 'abc'
      expect(last_response.status).to eq 400
      expect(last_response.body).to eq 'text is missing'
    end
  end

  context 'using a custom validator with message_key' do
    before do
      module CustomValidationsSpec
        class WithMessageKey < Grape::Validations::PresenceValidator
          def validate_param!(attr_name, _params)
            raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: :presence
          end
        end
      end
    end
    subject do
      Class.new(Grape::API) do
        params do
          requires :text, with_message_key: true
        end
        get do
          'bacon'
        end
      end
    end

    def app
      subject
    end

    it 'fails with message' do
      get '/', text: 'foobar'
      expect(last_response.status).to eq 400
      expect(last_response.body).to eq 'text is missing'
    end
  end

  context 'using a custom request/param validator' do
    before do
      module CustomValidationsSpec
        class Admin < Grape::Validations::Base
          def validate(request)
            # return if the param we are checking was not in request
            # @attrs is a list containing the attribute we are currently validating
            return unless request.params.key? @attrs.first
            # check if admin flag is set to true
            return unless @option
            # check if user is admin or not
            # as an example get a token from request and check if it's admin or not
            raise Grape::Exceptions::Validation, params: @attrs, message: 'Can not set Admin only field.' unless request.headers['X-Access-Token'] == 'admin'
          end
        end
      end
    end
    subject do
      Class.new(Grape::API) do
        params do
          optional :admin_field, type: String, admin: true
          optional :non_admin_field, type: String
          optional :admin_false_field, type: String, admin: false
        end
        get do
          'bacon'
        end
      end
    end

    def app
      subject
    end

    it 'fail when non-admin user sets an admin field' do
      get '/', admin_field: 'tester', non_admin_field: 'toaster'
      expect(last_response.status).to eq 400
      expect(last_response.body).to include 'Can not set Admin only field.'
    end

    it 'does not fail when we send non-admin fields only' do
      get '/', non_admin_field: 'toaster'
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end

    it 'does not fail when we send non-admin and admin=false fields only' do
      get '/', non_admin_field: 'toaster', admin_false_field: 'test'
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end

    it 'does not fail when we send admin fields and we are admin' do
      header 'X-Access-Token', 'admin'
      get '/', admin_field: 'tester', non_admin_field: 'toaster', admin_false_field: 'test'
      expect(last_response.status).to eq 200
      expect(last_response.body).to eq 'bacon'
    end

    it 'fails when we send admin fields and we are not admin' do
      header 'X-Access-Token', 'user'
      get '/', admin_field: 'tester', non_admin_field: 'toaster', admin_false_field: 'test'
      expect(last_response.status).to eq 400
      expect(last_response.body).to include 'Can not set Admin only field.'
    end
  end
end
