# frozen_string_literal: true

require 'spec_helper'

describe ActiveModel::Validations::FileContentTypeValidator do
  class Dummy
    include ActiveModel::Validations
  end

  subject { Dummy }

  def build_validator(options)
    @validator = described_class.new(options.merge(attributes: :avatar))
  end

  context 'whitelist format' do
    context 'with an allowed type' do
      context 'as a string' do
        before { build_validator allow: 'image/jpg' }
        it { is_expected.to allow_file_content_type('image/jpg', @validator) }
      end

      context 'as an regexp' do
        before { build_validator allow: /^image\/.*/ }
        it { is_expected.to allow_file_content_type('image/png', @validator) }
      end

      context 'as a list' do
        before { build_validator allow: ['image/png', 'image/jpg', 'image/jpeg'] }
        it { is_expected.to allow_file_content_type('image/png', @validator) }
      end

      context 'as a proc' do
        before { build_validator allow: ->(_record) { ['image/png', 'image/jpg', 'image/jpeg'] } }
        it { is_expected.to allow_file_content_type('image/png', @validator) }
      end
    end

    context 'with a disallowed type' do
      context 'as a string' do
        before { build_validator allow: 'image/png' }
        it { is_expected.not_to allow_file_content_type('image/jpeg', @validator) }
      end

      context 'as a regexp' do
        before { build_validator allow: /^text\/.*/ }
        it { is_expected.not_to allow_file_content_type('image/png', @validator) }
      end

      context 'as a proc' do
        before { build_validator allow: ->(_record) { /^text\/.*/ } }
        it { is_expected.not_to allow_file_content_type('image/png', @validator) }
      end

      context 'with :message option' do
        context 'without interpolation' do
          before do
            build_validator allow: 'image/png',
                            message: 'should be a PNG image'
          end

          it do
            is_expected.not_to allow_file_content_type(
              'image/jpeg', @validator,
              message: 'Avatar should be a PNG image'
            )
          end
        end

        context 'with interpolation' do
          before do
            build_validator allow: 'image/png',
                            message: 'should have content type %{types}'
          end

          it do
            is_expected.not_to allow_file_content_type(
              'image/jpeg', @validator,
              message: 'Avatar should have content type image/png'
            )
          end

          it do
            is_expected.to allow_file_content_type(
              'image/png', @validator,
              message: 'Avatar should have content type image/png'
            )
          end
        end
      end

      context 'default message' do
        before { build_validator allow: 'image/png' }

        it do
          is_expected.not_to allow_file_content_type(
            'image/jpeg', @validator,
            message: 'Avatar file should be one of image/png'
          )
        end
      end
    end
  end

  context 'blacklist format' do
    context 'with an allowed type' do
      context 'as a string' do
        before { build_validator exclude: 'image/gif' }
        it { is_expected.to allow_file_content_type('image/png', @validator) }
      end

      context 'as an regexp' do
        before { build_validator exclude: /^text\/.*/ }
        it { is_expected.to allow_file_content_type('image/png', @validator) }
      end

      context 'as a list' do
        before { build_validator exclude: ['image/png', 'image/jpg', 'image/jpeg'] }
        it { is_expected.to allow_file_content_type('image/gif', @validator) }
      end

      context 'as a proc' do
        before { build_validator exclude: ->(_record) { ['image/png', 'image/jpg', 'image/jpeg'] } }
        it { is_expected.to allow_file_content_type('image/gif', @validator) }
      end
    end

    context 'with a disallowed type' do
      context 'as a string' do
        before { build_validator exclude: 'image/gif' }
        it { is_expected.not_to allow_file_content_type('image/gif', @validator) }
      end

      context 'as an regexp' do
        before { build_validator exclude: /^text\/.*/ }
        it { is_expected.not_to allow_file_content_type('text/plain', @validator) }
      end

      context 'as an proc' do
        before { build_validator exclude: ->(_record) { /^text\/.*/ } }
        it { is_expected.not_to allow_file_content_type('text/plain', @validator) }
      end

      context 'with :message option' do
        context 'without interpolation' do
          before do
            build_validator exclude: 'image/png',
                            message: 'should not be a PNG image'
          end

          it do
            is_expected.not_to allow_file_content_type(
              'image/png', @validator,
              message: 'Avatar should not be a PNG image'
            )
          end
        end

        context 'with interpolation' do
          before do
            build_validator exclude: 'image/png',
                            message: 'should not have content type %{types}'
          end

          it do
            is_expected.not_to allow_file_content_type(
              'image/png', @validator,
              message: 'Avatar should not have content type image/png'
            )
          end

          it do
            is_expected.to allow_file_content_type(
              'image/jpeg', @validator,
              message: 'Avatar should not have content type image/jpeg'
            )
          end
        end
      end

      context 'default message' do
        before { build_validator exclude: 'image/png' }

        it do
          is_expected.not_to allow_file_content_type(
            'image/png', @validator,
            message: 'Avatar file cannot be image/png'
          )
        end
      end
    end
  end

  context 'using the helper' do
    before { Dummy.validates_file_content_type :avatar, allow: 'image/jpg' }

    it 'adds the validator to the class' do
      expect(Dummy.validators_on(:avatar)).to include(described_class)
    end
  end

  context 'given options' do
    it 'raises argument error if no required argument was given' do
      expect { build_validator message: 'Some message' }.to raise_error(ArgumentError)
    end

    described_class::CHECKS.each do |argument|
      it "does not raise error if :#{argument} is string, array, regexp or a proc" do
        expect { build_validator argument => 'image/jpg' }.not_to raise_error
        expect { build_validator argument => ['image/jpg'] }.not_to raise_error
        expect { build_validator argument => /^image\/.*/ }.not_to raise_error
        expect { build_validator argument => ->(_record) { 'image/jpg' } }.not_to raise_error
      end

      it "raises argument error if :#{argument} is neither a string, array, regexp nor proc" do
        expect { build_validator argument => 5.kilobytes }.to raise_error(ArgumentError)
      end
    end
  end
end
