thoughtbot--shoulda-matchers/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spe...

859 lines
29 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require 'unit_spec_helper'
describe Shoulda::Matchers::ActiveRecord::DefineEnumForMatcher, type: :model do
context 'if the attribute is given in plural form accidentally' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attrs as an enum, but no such enum exists on
Example.
MESSAGE
assertion = lambda do
expect(record).to define_enum_for(:attrs)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if a method to hold enum values exists on the model but was not created via the enum macro' do
it 'rejects with an appropriate failure message' do
model = define_model 'Example' do
def self.statuses; end
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum, but no such enum exists on
Example.
MESSAGE
assertion = lambda do
expect(model.new).to define_enum_for(:attr)
end
expect(&assertion).to fail_with_message(message)
end
end
describe 'with only the attribute name specified' do
context 'if the attribute is not defined as an enum' do
it 'rejects with an appropriate failure message' do
record = build_record_with_non_enum_attribute(
model_name: 'Example',
attribute_name: :attr,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum, but no such enum exists
on Example.
MESSAGE
assertion = lambda do
expect(record).to define_enum_for(:attr)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if the column storing the attribute is not an integer type' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :string,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer.
However, :attr is a string column.
MESSAGE
assertion = lambda do
expect(record).to define_enum_for(:attr)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute is defined as an enum' do
it 'matches' do
record = build_record_with_array_values(attribute_name: :attr)
expect { define_enum_for(:attr) }.
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer
MESSAGE
end
end
end
describe 'with both attribute name and enum values specified' do
context 'when the actual enum values are an array' do
context 'if the attribute is not defined as an enum' do
it 'rejects with an appropriate failure message' do
record = build_record_with_non_enum_attribute(
model_name: 'Example',
attribute_name: :attr,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum, but no such enum
exists on Example.
MESSAGE
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values(['open', 'close'])
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute is defined as an enum' do
context 'but the enum values do not match' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: ['published', 'unpublished', 'draft'],
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "open" to 0 and "close" to 1. However, :attr
actually maps "published" to 0, "unpublished" to 1, and
"draft" to 2.
MESSAGE
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values(['open', 'close'])
end
expect(&assertion).to fail_with_message(message)
end
end
context 'and the enum values match' do
it 'matches' do
record = build_record_with_array_values(
attribute_name: :attr,
values: ['published', 'unpublished', 'draft'],
)
matcher = lambda do
define_enum_for(:attr).
with_values(['published', 'unpublished', 'draft'])
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "published" to 0, "unpublished" to 1,
and "draft" to 2, but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values(['published', 'unpublished', 'draft'])
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values ["published", "unpublished", "draft"]
MESSAGE
end
end
end
end
context 'when the actual enum values are a hash' do
context 'if the attribute is not defined as an enum' do
it 'rejects with an appropriate failure message' do
record = build_record_with_non_enum_attribute(
model_name: 'Example',
attribute_name: :attr,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum, but no such enum exists
on Example.
MESSAGE
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values(active: 5, archived: 10)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute is defined as an enum' do
context 'but the enum values do not match' do
it 'rejects with an appropriate failure message' do
record = build_record_with_hash_values(
model_name: 'Example',
attribute_name: :attr,
values: { active: 0, archived: 1 },
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 5 and "archived" to 10. However, :attr
actually maps "active" to 0 and "archived" to 1.
MESSAGE
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values(active: 5, archived: 10)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'and the enum values match' do
context 'when expected enum values are a hash' do
it 'matches' do
record = build_record_with_hash_values(
attribute_name: :attr,
values: { active: 0, archived: 1 },
)
matcher = lambda do
define_enum_for(:attr).
with_values(active: 0, archived: 1)
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1,
but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values(active: 0, archived: 1)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values {active: 0, archived: 1}
MESSAGE
end
end
context 'when expected enum values are an array' do
it 'matches' do
record = build_record_with_hash_values(
attribute_name: :attr,
values: { active: 0, archived: 1 },
)
matcher = lambda do
define_enum_for(:attr).
with_values(['active', 'archived'])
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1,
but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values(['active', 'archived'])
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values ["active", "archived"]
MESSAGE
end
end
end
end
end
end
context 'with values specified using #with' do
it 'produces a warning' do
record = build_record_with_array_values(
attribute_name: :attr,
values: [:foo, :bar],
)
assertion = lambda do
expect(record).to define_enum_for(:attr).with([:foo, :bar])
end
expect(&assertion).to deprecate(
'The `with` qualifier on `define_enum_for`',
'`with_values`',
)
end
end
describe 'with the backing column specified to be of some type' do
context 'if the column storing the attribute is of a different type' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
)
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by a string.
However, :attr is an integer column.
MESSAGE
assertion = lambda do
expect(record).
to define_enum_for(:attr).
backed_by_column_of_type(:string)
end
expect(&assertion).to fail_with_message(message)
end
end
context 'if the column storing the attribute is of the same type' do
it 'matches' do
record = build_record_with_array_values(
attribute_name: :attr,
column_type: :string,
)
matcher = lambda do
define_enum_for(:attr).backed_by_column_of_type(:string)
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by a string,
but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).backed_by_column_of_type(:string)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by a string
MESSAGE
end
end
end
if active_record_enum_supports_prefix_and_suffix?
context 'qualified with #with_prefix' do
context 'when the prefix is explicit' do
context 'if the attribute was not defined with a prefix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and prefixing
accessor methods with "foo_". :attr does map to these values, but
the enum is configured with either a different prefix or no prefix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with a different prefix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
prefix: :foo,
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:bar)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and prefixing
accessor methods with "bar_". :attr does map to these values, but
the enum is configured with either a different prefix or no prefix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with the same prefix' do
it 'matches' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: [:active, :archived],
prefix: :foo,
)
matcher = lambda do
define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo)
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1 and
prefixing accessor methods with "foo_", but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values [:active, :archived], prefix: :foo
MESSAGE
end
end
end
context 'when the prefix is implicit' do
context 'if the attribute was not defined with a prefix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and prefixing
accessor methods with "attr_". :attr does map to these values, but
the enum is configured with either a different prefix or no prefix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with a prefix' do
it 'matches' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: [:active, :archived],
prefix: true,
)
matcher = lambda do
define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1 and
prefixing accessor methods with "attr_", but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values [:active, :archived], prefix: true
MESSAGE
end
end
end
end
context 'qualified with #with_suffix' do
context 'when the suffix is explicit' do
context 'if the attribute was not defined with a suffix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix(:foo)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and suffixing
accessor methods with "_foo". :attr does map to these values, but
the enum is configured with either a different suffix or no suffix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with a different suffix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
suffix: :foo,
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix(:bar)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and suffixing
accessor methods with "_bar". :attr does map to these values, but
the enum is configured with either a different suffix or no suffix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with the same suffix' do
it 'matches' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: [:active, :archived],
suffix: :foo,
)
matcher = lambda do
define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix(:foo)
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1 and
suffixing accessor methods with "_foo", but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix(:foo)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values [:active, :archived], suffix: :foo
MESSAGE
end
end
end
context 'when the suffix is implicit' do
context 'if the attribute was not defined with a suffix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1 and suffixing
accessor methods with "_attr". :attr does map to these values, but
the enum is configured with either a different suffix or no suffix
at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with a suffix' do
it 'matches' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: [:active, :archived],
suffix: true,
)
matcher = lambda do
define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1 and
suffixing accessor methods with "_attr", but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values([:active, :archived]).
with_suffix
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values [:active, :archived], suffix: true
MESSAGE
end
end
end
end
context 'qualified with both #with_prefix and #with_suffix' do
context 'if the attribute was not defined with a different prefix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
prefix: :foo,
suffix: :bar,
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:whatever).
with_suffix(:bar)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1, prefixing
accessor methods with "whatever_", and suffixing accessor methods
with "_bar". :attr does map to these values, but the enum is
configured with either a different prefix or suffix, or no prefix or
suffix at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
context 'if the attribute was defined with a different suffix' do
it 'rejects with an appropriate failure message' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: [:active, :archived],
prefix: :foo,
suffix: :bar,
)
assertion = lambda do
expect(record).
to define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo).
with_suffix(:whatever)
end
message = format_message(<<-MESSAGE)
Expected Example to define :attr as an enum backed by an integer,
mapping "active" to 0 and "archived" to 1, prefixing
accessor methods with "foo_", and suffixing accessor methods with
"_whatever". :attr does map to these values, but the enum is
configured with either a different prefix or suffix, or no prefix
or suffix at all (we can't tell which).
MESSAGE
expect(&assertion).to fail_with_message(message)
end
end
context 'if the attribute was defined with the same prefix and suffix' do
it 'matches' do
record = build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
values: [:active, :archived],
prefix: :foo,
suffix: :bar,
)
matcher = lambda do
define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo).
with_suffix(:bar)
end
expect(&matcher).
to match_against(record).
or_fail_with(<<-MESSAGE, wrap: true)
Expected Example not to define :attr as an enum backed by an
integer, mapping "active" to 0 and "archived" to 1,
prefixing accessor methods with "foo_", and suffixing accessor
methods with "_bar", but it did.
MESSAGE
end
it 'has the right description' do
matcher = define_enum_for(:attr).
with_values([:active, :archived]).
with_prefix(:foo).
with_suffix(:bar)
expect(matcher.description).to eq(<<~MESSAGE.strip)
define :attr as an enum backed by an integer with values [:active, :archived], prefix: :foo, suffix: :bar
MESSAGE
end
end
end
end
end
def build_record_with_array_values(
model_name: 'Example',
attribute_name: :attr,
column_type: :integer,
values: ['published', 'unpublished', 'draft'],
prefix: false,
suffix: false
)
build_record_with_enum_attribute(
model_name: model_name,
attribute_name: attribute_name,
column_type: column_type,
values: values,
prefix: prefix,
suffix: suffix,
)
end
def build_record_with_hash_values(
model_name: 'Example',
attribute_name: :attr,
values: { active: 0, archived: 1 },
prefix: false,
suffix: false
)
build_record_with_enum_attribute(
model_name: model_name,
attribute_name: attribute_name,
column_type: :integer,
values: values,
prefix: prefix,
suffix: suffix,
)
end
def build_record_with_enum_attribute(
model_name:,
attribute_name:,
column_type:,
values:,
prefix: false,
suffix: false
)
model = define_model(
model_name,
attribute_name => { type: column_type },
)
if active_record_enum_supports_prefix_and_suffix?
model.enum(attribute_name => values, _prefix: prefix, _suffix: suffix)
else
model.enum(attribute_name => values)
end
model.new
end
def build_record_with_non_enum_attribute(model_name:, attribute_name:)
define_model(model_name, attribute_name => :integer).new
end
end