require 'unit_spec_helper'

describe Shoulda::Matchers::ActiveModel::DisallowValueMatcher, type: :model do
  context 'an attribute with a format validation' do
    it 'does not match if the value is allowed' do
      expect(validating_format(with: /abc/)).not_to matcher('abcde').for(:attr)
    end

    it 'matches if the value is not allowed' do
      expect(validating_format(with: /abc/)).to matcher('xyz').for(:attr)
    end
  end

  context 'an attribute with a context-dependent validation' do
    context 'without the validation context' do
      it 'does not match' do
        expect(validating_format(with: /abc/, on: :customisable)).not_to matcher('xyz').for(:attr)
      end
    end

    context 'with the validation context' do
      it 'disallows a bad value' do
        expect(validating_format(with: /abc/, on: :customisable)).to matcher('xyz').for(:attr).on(:customisable)
      end

      it 'does not match a good value' do
        expect(validating_format(with: /abc/, on: :customisable)).not_to matcher('abcde').for(:attr).on(:customisable)
      end
    end
  end

  context 'an attribute with a format validation and a custom message' do
    it 'does not match if the value and message are both correct' do
      expect(validating_format(with: /abc/, message: 'good message')).
        not_to matcher('abcde').for(:attr).with_message('good message')
    end

    it "delegates its failure message to its allow matcher's negative failure message" do
      allow_matcher = double(
        'allow_matcher',
        failure_message_when_negated: 'allow matcher failure',
      ).as_null_object
      allow(Shoulda::Matchers::ActiveModel::AllowValueMatcher).
        to receive(:new).
        and_return(allow_matcher)

      matcher = matcher('abcde').for(:attr).with_message('good message')
      matcher.matches?(validating_format(with: /abc/, message: 'good message'))

      expect(matcher.failure_message).to eq 'allow matcher failure'
    end

    it 'matches if the message is correct but the value is not' do
      expect(validating_format(with: /abc/, message: 'good message')).
        to matcher('xyz').for(:attr).with_message('good message')
    end
  end

  context 'an attribute where the message occurs on another attribute' do
    it 'matches if the message is correct but the value is not' do
      expect(record_with_custom_validation).to \
        matcher('bad value').for(:attr).with_message(/some message/, against: :attr2)
    end

    it 'does not match if the value and message are both correct' do
      expect(record_with_custom_validation).not_to \
        matcher('good value').for(:attr).with_message(/some message/, against: :attr2)
    end

    def record_with_custom_validation
      define_model :example, attr: :string, attr2: :string do
        validate :custom_validation

        def custom_validation # rubocop:disable Lint/NestedMethodDefinition
          if self[:attr] != 'good value'
            errors.add :attr2, 'some message'
          end
        end
      end.new
    end
  end

  def matcher(value)
    described_class.new(value)
  end
  alias_method :disallow_value, :matcher
end
