thoughtbot--shoulda-matchers/spec/unit/shoulda/matchers/active_model/validate_numericality_of_ma...

778 lines
23 KiB
Ruby
Raw Normal View History

require 'unit_spec_helper'
describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher, type: :model do
class << self
def all_qualifiers
[
{
category: :comparison,
name: :is_greater_than,
argument: 1,
validation_name: :greater_than,
validation_value: 1,
},
{
category: :comparison,
name: :is_greater_than_or_equal_to,
argument: 1,
validation_name: :greater_than_or_equal_to,
validation_value: 1,
},
{
category: :comparison,
name: :is_less_than,
argument: 1,
validation_name: :less_than,
validation_value: 1,
},
{
category: :comparison,
name: :is_less_than_or_equal_to,
argument: 1,
validation_name: :less_than_or_equal_to,
validation_value: 1,
},
{
category: :comparison,
name: :is_equal_to,
argument: 1,
validation_name: :equal_to,
validation_value: 1,
},
{
category: :cardinality,
name: :odd,
validation_name: :odd,
validation_value: true,
},
{
category: :cardinality,
name: :even,
validation_name: :even,
validation_value: true,
},
{
name: :only_integer,
validation_name: :only_integer,
validation_value: true,
}
]
end
def qualifiers_under(category)
all_qualifiers.select do |qualifier|
qualifier[:category] == category
end
end
def mutually_exclusive_qualifiers
qualifiers_under(:cardinality) + qualifiers_under(:comparison)
end
def non_mutually_exclusive_qualifiers
all_qualifiers - mutually_exclusive_qualifiers
end
def validations_by_qualifier
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:name]] = qualifier[:validation_name]
end
end
def all_qualifier_combinations
combinations = []
([nil] + mutually_exclusive_qualifiers).each do |mutually_exclusive_qualifier|
(0..non_mutually_exclusive_qualifiers.length).each do |n|
non_mutually_exclusive_qualifiers.combination(n) do |combination|
super_combination = (
[mutually_exclusive_qualifier] +
combination
)
super_combination.select!(&:present?)
if super_combination.any?
combinations << super_combination
end
end
end
end
combinations
end
def default_qualifier_arguments
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:name]] = qualifier[:argument]
end
end
def default_validation_values
all_qualifiers.each_with_object({}) do |qualifier, hash|
hash[qualifier[:validation_name]] = qualifier[:validation_value]
end
end
end
context 'qualified with nothing' do
context 'and validating numericality' do
it 'accepts' do
record = build_record_validating_numericality
expect(record).to validate_numericality
end
end
context 'and not validating anything' do
it 'rejects since it does not disallow non-numbers' do
record = build_record_validating_nothing
assertion = -> { expect(record).to validate_numericality }
expect(&assertion).to fail_with_message_including(
'Expected errors to include "is not a number"'
)
end
end
end
context 'qualified with allow_nil' do
context 'and validating with allow_nil' do
it 'accepts' do
record = build_record_validating_numericality(allow_nil: true)
expect(record).to validate_numericality.allow_nil
end
end
context 'and not validating with allow_nil' do
it 'rejects since it tries to treat nil as a number' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.allow_nil
end
expect(&assertion).to fail_with_message_including(
%[Did not expect errors to include "is not a number" when #{attribute_name} is set to nil]
)
end
end
end
context 'qualified with only_integer' do
context 'and validating with only_integer' do
it 'accepts' do
record = build_record_validating_numericality(only_integer: true)
expect(record).to validate_numericality.only_integer
end
end
context 'and not validating with only_integer' do
it 'rejects since it does not disallow non-integers' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.only_integer
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be an integer"'
)
end
end
end
context 'qualified with odd' do
context 'and validating with odd' do
it 'accepts' do
record = build_record_validating_numericality(odd: true)
expect(record).to validate_numericality.odd
end
end
context 'and not validating with odd' do
it 'rejects since it does not disallow even numbers' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.odd
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be odd"'
)
end
end
end
context 'qualified with even' do
context 'and validating with even' do
it 'allows even number values for that attribute' do
record = build_record_validating_numericality(even: true)
expect(record).to validate_numericality.even
end
end
context 'and not validating with even' do
it 'rejects since it does not disallow odd numbers' do
record = build_record_validating_numericality
assertion = -> { expect(record).to validate_numericality.even }
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be even"'
)
end
end
end
context 'qualified with is_less_than_or_equal_to' do
context 'and validating with less_than_or_equal_to' do
it 'accepts' do
record = build_record_validating_numericality(
less_than_or_equal_to: 18
)
expect(record).to validate_numericality.is_less_than_or_equal_to(18)
end
context 'if the given value is right at the allowed max value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2,
less_than_or_equal_to: 32767
)
assertion = lambda do
expect(record).
to validate_numericality.
is_less_than_or_equal_to(32767)
end
expect(&assertion).not_to raise_error
end
end
end
context 'and not validating with less_than_or_equal_to' do
it 'rejects since it does not disallow numbers greater than the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_less_than_or_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be less than or equal to 18"'
)
end
end
end
context 'qualified with is_less_than' do
context 'and validating with less_than' do
it 'accepts' do
record = build_record_validating_numericality(less_than: 18)
expect(record).
to validate_numericality.
is_less_than(18)
end
context 'if the given value is right at the allowed max value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2, less_than: 32767)
assertion = lambda do
expect(record).to validate_numericality.is_less_than(32767)
end
expect(&assertion).not_to raise_error
end
end
end
context 'and not validating with less_than' do
it 'rejects since it does not disallow numbers greater than or equal to the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_less_than(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be less than 18"'
)
end
end
end
context 'qualified with is_equal_to' do
context 'and validating with equal_to' do
it 'accepts' do
record = build_record_validating_numericality(equal_to: 18)
expect(record).to validate_numericality.is_equal_to(18)
end
context 'if the given value is right at the allowed min value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2, equal_to: -32768)
assertion = lambda do
expect(record).to validate_numericality.is_equal_to(-32768)
end
expect(&assertion).not_to raise_error
end
end
context 'if the given value is right at the allowed max value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2, equal_to: 32767)
assertion = lambda do
expect(record).to validate_numericality.is_equal_to(32767)
end
expect(&assertion).not_to raise_error
end
end
end
context 'and not validating with equal_to' do
it 'rejects since it does not disallow numbers that are not the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).to validate_numericality.is_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be equal to 18"'
)
end
end
end
context 'qualified with is_greater_than_or_equal to' do
context 'validating with greater_than_or_equal_to' do
it 'accepts' do
record = build_record_validating_numericality(
greater_than_or_equal_to: 18
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
context 'if the given value is right at the allowed min value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2,
greater_than_or_equal_to: -32768
)
assertion = lambda do
expect(record).
to validate_numericality_of(:attr).
is_greater_than_or_equal_to(-32768)
end
expect(&assertion).not_to raise_error
end
end
end
context 'not validating with greater_than_or_equal_to' do
it 'rejects since it does not disallow numbers that are less than the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be greater than or equal to 18"'
)
end
end
end
context 'qualified with is_greater_than' do
context 'and validating with greater_than' do
it 'accepts' do
record = build_record_validating_numericality(greater_than: 18)
expect(record).
to validate_numericality.
is_greater_than(18)
end
context 'if the given value is right at the allowed min value for the column' do
it 'does not raise an error' do
record = build_record_with_integer_column_of_limit(2,
greater_than: -32768
)
assertion = lambda do
expect(record).
to validate_numericality_of(:attr).
is_greater_than(-32768)
end
expect(&assertion).not_to raise_error
end
end
end
context 'and not validating with greater_than' do
it 'rejects since it does not disallow numbers that are less than or equal to the value' do
record = build_record_validating_numericality
assertion = lambda do
expect(record).
to validate_numericality.
is_greater_than(18)
end
expect(&assertion).to fail_with_message_including(
'Expected errors to include "must be greater than 18"'
)
end
end
end
context 'qualified with with_message' do
context 'and validating with the same message' do
it 'accepts' do
record = build_record_validating_numericality(message: 'custom')
expect(record).to validate_numericality.with_message(/custom/)
end
end
context 'and validating with a different message' do
it 'rejects' do
record = build_record_validating_numericality(message: 'custom')
expect(record).not_to validate_numericality.with_message(/wrong/)
end
end
context 'and no message is provided' do
it 'ignores the qualifier' do
record = build_record_validating_numericality
expect(record).to validate_numericality.with_message(nil)
end
end
end
context 'with combinations of qualifiers together' do
all_qualifier_combinations.each do |combination|
if combination.size > 1
it do
validation_options = build_validation_options(for: combination)
record = build_record_validating_numericality(validation_options)
validate_numericality = self.validate_numericality
apply_qualifiers!(for: combination, to: validate_numericality)
expect(record).to validate_numericality
end
end
end
context 'when the qualifiers do not match the validation options' do
it do
record = build_record_validating_numericality(
even: true,
greater_than: 18
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(greater_than: 18)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
even: true,
greater_than_or_equal_to: 18
)
expect(record).
not_to validate_numericality.
even.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
odd: true,
greater_than: 18
)
expect(record).
not_to validate_numericality.
even.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
odd: true,
greater_than_or_equal_to: 99
)
expect(record).
not_to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
it do
record = build_record_validating_numericality(
only_integer: true,
greater_than_or_equal_to: 18,
less_than: 99
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
end
context 'when qualifiers match the validation options but the values are different' do
it do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 19
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 17
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
even: true,
greater_than: 20
)
expect(record).
not_to validate_numericality.
even.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
even: true,
greater_than: 16
)
expect(record).
not_to validate_numericality.
even.
is_greater_than(18)
end
it do
record = build_record_validating_numericality(
odd: true,
less_than_or_equal_to: 101
)
expect(record).
not_to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
it do
record = build_record_validating_numericality(
odd: true,
less_than_or_equal_to: 97
)
expect(record).
not_to validate_numericality.
odd.
is_less_than_or_equal_to(99)
end
it do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 19,
less_than: 99
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
it do
record = build_record_validating_numericality(
only_integer: true,
greater_than: 18,
less_than: 100
)
expect(record).
not_to validate_numericality.
only_integer.
is_greater_than(18).
is_less_than(99)
end
end
end
context 'with large numbers' do
it do
record = build_record_validating_numericality(greater_than: 100_000)
expect(record).to validate_numericality.is_greater_than(100_000)
end
it do
record = build_record_validating_numericality(less_than: 100_000)
expect(record).to validate_numericality.is_less_than(100_000)
end
it do
record = build_record_validating_numericality(
greater_than_or_equal_to: 100_000
)
expect(record).
to validate_numericality.
is_greater_than_or_equal_to(100_000)
end
it do
record = build_record_validating_numericality(
less_than_or_equal_to: 100_000
)
expect(record).
to validate_numericality.
is_less_than_or_equal_to(100_000)
end
2012-12-20 05:04:27 +00:00
end
context 'when the subject is stubbed' do
it 'retains that stub while the validate_numericality is matching' do
model = define_model :example, attr: :string do
2014-01-17 20:20:44 +00:00
validates_numericality_of :attr, odd: true
before_validation :set_attr!
def set_attr!; self.attr = 5 end
end
record = model.new
allow(record).to receive(:set_attr!)
expect(record).to validate_numericality_of(:attr).odd
end
end
describe '#description' do
context 'qualified with nothing' do
it 'describes that it allows numbers' do
matcher = validate_numericality_of(:attr)
expect(matcher.description).to eq 'only allow numbers for attr'
end
end
context 'qualified with only_integer' do
it 'describes that it allows integers' do
matcher = validate_numericality_of(:attr).only_integer
expect(matcher.description).to eq 'only allow integers for attr'
end
end
qualifiers_under(:cardinality).each do |qualifier|
context "qualified with #{qualifier[:name]}" do
it "describes that it allows #{qualifier[:name]} numbers" do
matcher = validate_numericality_of(:attr).__send__(qualifier[:name])
expect(matcher.description).
to eq "only allow #{qualifier[:name]} numbers for attr"
end
end
end
qualifiers_under(:comparison).each do |qualifier|
comparison_phrase = qualifier[:validation_name].to_s.gsub('_', ' ')
context "qualified with #{qualifier[:name]}" do
it "describes that it allows numbers #{comparison_phrase} a certain value" do
matcher = validate_numericality_of(:attr).
__send__(qualifier[:name], 18)
expect(matcher.description).to eq(
"only allow numbers for attr which are #{comparison_phrase} 18"
)
end
end
end
context 'qualified with odd + is_greater_than_or_equal_to' do
it "describes that it allows odd numbers greater than or equal to a certain value" do
matcher = validate_numericality_of(:attr).
odd.
is_greater_than_or_equal_to(18)
expect(matcher.description).to eq(
'only allow odd numbers for attr which are greater than or equal to 18'
)
end
end
context 'qualified with only integer + is_greater_than + less_than_or_equal_to' do
it 'describes that it allows integer greater than one value and less than or equal to another' do
matcher = validate_numericality_of(:attr).
only_integer.
is_greater_than(18).
is_less_than_or_equal_to(100)
expect(matcher.description).to eq(
'only allow integers for attr which are greater than 18 and less than or equal to 100'
)
end
end
end
def build_validation_options(args)
combination = args.fetch(:for)
combination.each_with_object({}) do |qualifier, hash|
value = self.class.default_validation_values.fetch(qualifier[:validation_name])
hash[qualifier[:validation_name]] = value
end
end
def apply_qualifiers!(args)
combination = args.fetch(:for)
matcher = args.fetch(:to)
combination.each do |qualifier|
args = self.class.default_qualifier_arguments.fetch(qualifier[:name])
matcher.__send__(qualifier[:name], *args)
end
end
def define_model_validating_numericality(options = {})
define_model 'Example', attribute_name => :string do |model|
model.validates_numericality_of(attribute_name, options)
end
end
def build_record_validating_numericality(options = {})
define_model_validating_numericality(options).new
end
def define_model_validating_nothing
define_model('Example', attribute_name => :string)
end
def build_record_validating_nothing
define_model_validating_nothing.new
end
def validate_numericality
validate_numericality_of(attribute_name)
end
def attribute_name
:attr
end
def define_model_with_integer_column_of_limit(limit, validation_options = {})
column_options = { type: :integer, options: { limit: limit } }
define_model :example, attribute_name => column_options do |model|
model.validates_numericality_of(attribute_name, validation_options)
end
end
def build_record_with_integer_column_of_limit(limit, validation_options = {})
define_model_with_integer_column_of_limit(limit, validation_options).new
end
end