2022-01-01 13:38:12 +00:00
|
|
|
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2018-08-24 01:39:17 +00:00
|
|
|
require 'rails_helper'
|
|
|
|
|
|
|
|
RSpec.describe ObjectManager::Attribute, type: :model do
|
2020-01-18 11:01:51 +00:00
|
|
|
|
2018-08-24 01:39:17 +00:00
|
|
|
describe 'callbacks' do
|
|
|
|
context 'for setting default values on local data options' do
|
2021-09-22 06:53:16 +00:00
|
|
|
subject(:attr) { described_class.new }
|
2018-08-24 01:39:17 +00:00
|
|
|
|
|
|
|
context ':null' do
|
|
|
|
it 'sets nil values to true' do
|
2021-09-22 06:53:16 +00:00
|
|
|
expect { attr.validate }
|
|
|
|
.to change { attr.data_option[:null] }.to(true)
|
2018-08-24 01:39:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not overwrite false values' do
|
2021-09-22 06:53:16 +00:00
|
|
|
attr.data_option[:null] = false
|
2018-08-24 01:39:17 +00:00
|
|
|
|
2021-09-22 06:53:16 +00:00
|
|
|
expect { attr.validate }
|
|
|
|
.not_to change { attr.data_option[:null] }
|
2018-08-24 01:39:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context ':maxlength' do
|
|
|
|
context 'for data_type: select / tree_select / checkbox' do
|
2021-09-22 06:53:16 +00:00
|
|
|
subject(:attr) { described_class.new(data_type: 'select') }
|
2018-08-24 01:39:17 +00:00
|
|
|
|
|
|
|
it 'sets nil values to 255' do
|
2021-09-22 06:53:16 +00:00
|
|
|
expect { attr.validate }
|
|
|
|
.to change { attr.data_option[:maxlength] }.to(255)
|
2018-08-24 01:39:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context ':nulloption' do
|
|
|
|
context 'for data_type: select / tree_select / checkbox' do
|
2021-09-22 06:53:16 +00:00
|
|
|
subject(:attr) { described_class.new(data_type: 'select') }
|
2018-08-24 01:39:17 +00:00
|
|
|
|
|
|
|
it 'sets nil values to true' do
|
2021-09-22 06:53:16 +00:00
|
|
|
expect { attr.validate }
|
|
|
|
.to change { attr.data_option[:nulloption] }.to(true)
|
2018-08-24 01:39:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not overwrite false values' do
|
2021-09-22 06:53:16 +00:00
|
|
|
attr.data_option[:nulloption] = false
|
2018-08-24 01:39:17 +00:00
|
|
|
|
|
|
|
expect { subject.validate }
|
|
|
|
.not_to change { subject.data_option[:nulloption] }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-09-14 14:32:02 +00:00
|
|
|
|
|
|
|
describe 'check name' do
|
|
|
|
it 'rejects ActiveRecord reserved word "attribute"' do
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attributes_for :object_manager_attribute_text, name: 'attribute'
|
2020-02-13 11:55:19 +00:00
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Name attribute is a reserved word! (2)')
|
2018-09-14 14:32:02 +00:00
|
|
|
end
|
|
|
|
|
2019-09-02 16:20:32 +00:00
|
|
|
%w[destroy true false integer select drop create alter index table varchar blob date datetime timestamp url icon initials avatar permission validate subscribe unsubscribe translate search _type _doc _id id].each do |reserved_word|
|
2019-07-31 07:45:27 +00:00
|
|
|
it "rejects Zammad reserved word '#{reserved_word}'" do
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attributes_for :object_manager_attribute_text, name: reserved_word
|
2021-05-12 11:37:44 +00:00
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, %r{is a reserved word! \(1\)})
|
2019-07-31 07:45:27 +00:00
|
|
|
end
|
2018-09-14 14:32:02 +00:00
|
|
|
end
|
|
|
|
|
2019-09-02 16:20:32 +00:00
|
|
|
%w[someting_id something_ids].each do |reserved_word|
|
|
|
|
it "rejects word '#{reserved_word}' which is used for database references" do
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attributes_for :object_manager_attribute_text, name: reserved_word
|
2020-02-13 11:55:19 +00:00
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name can't get used because *_id and *_ids are not allowed")
|
2019-09-02 16:20:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-26 12:33:06 +00:00
|
|
|
%w[title tags number].each do |not_editable_attribute|
|
2021-10-19 08:38:20 +00:00
|
|
|
it "rejects '#{not_editable_attribute}' which is used" do
|
|
|
|
expect do
|
|
|
|
described_class.add attributes_for :object_manager_attribute_text, name: not_editable_attribute
|
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Name Attribute not editable!')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-26 12:33:06 +00:00
|
|
|
%w[priority state note].each do |existing_attribute|
|
2021-10-19 08:38:20 +00:00
|
|
|
it "rejects '#{existing_attribute}' which is used" do
|
|
|
|
expect do
|
|
|
|
described_class.add attributes_for :object_manager_attribute_text, name: existing_attribute
|
|
|
|
end.to raise_error(ActiveRecord::RecordInvalid, "Validation failed: Name #{existing_attribute} already exists!")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-09-14 14:32:02 +00:00
|
|
|
it 'rejects duplicate attribute name of conflicting types' do
|
|
|
|
attribute = attributes_for :object_manager_attribute_text
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2018-09-14 14:32:02 +00:00
|
|
|
attribute[:data_type] = 'boolean'
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2018-09-14 14:32:02 +00:00
|
|
|
end.to raise_error ActiveRecord::RecordInvalid
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts duplicate attribute name on the same types (editing an existing attribute)' do
|
|
|
|
attribute = attributes_for :object_manager_attribute_text
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2018-09-14 14:32:02 +00:00
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2019-04-15 01:41:17 +00:00
|
|
|
end.not_to raise_error
|
2018-09-14 14:32:02 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts duplicate attribute name on compatible types (editing the type of an existing attribute)' do
|
|
|
|
attribute = attributes_for :object_manager_attribute_text
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2018-09-14 14:32:02 +00:00
|
|
|
attribute[:data_type] = 'select'
|
|
|
|
attribute[:data_option_new] = { default: '', options: { 'a' => 'a' } }
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attribute
|
2019-04-15 01:41:17 +00:00
|
|
|
end.not_to raise_error
|
2018-09-14 14:32:02 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'accepts valid attribute names' do
|
|
|
|
expect do
|
2019-09-16 15:04:17 +00:00
|
|
|
described_class.add attributes_for :object_manager_attribute_text
|
2019-04-15 01:41:17 +00:00
|
|
|
end.not_to raise_error
|
2018-09-14 14:32:02 +00:00
|
|
|
end
|
|
|
|
end
|
2020-11-24 09:32:07 +00:00
|
|
|
|
|
|
|
describe 'validate that referenced attributes are not set as inactive' do
|
|
|
|
subject(:attr) { create(:object_manager_attribute_text) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(described_class)
|
|
|
|
.to receive(:attribute_used_by_references?)
|
|
|
|
.with(attr.object_lookup.name, attr.name)
|
|
|
|
.and_return(is_referenced)
|
|
|
|
|
|
|
|
attr.active = active
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when is used and changing to inactive' do
|
|
|
|
let(:active) { false }
|
|
|
|
let(:is_referenced) { true }
|
|
|
|
|
|
|
|
it { is_expected.not_to be_valid }
|
|
|
|
|
|
|
|
it do
|
|
|
|
attr.valid?
|
|
|
|
expect(attr.errors).not_to be_blank
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when is not used and changing to inactive' do
|
|
|
|
let(:active) { false }
|
|
|
|
let(:is_referenced) { false }
|
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when is used and staying active and chan' do
|
|
|
|
let(:active) { true }
|
|
|
|
let(:is_referenced) { true }
|
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
|
|
|
end
|
2021-10-01 19:23:20 +00:00
|
|
|
|
|
|
|
describe 'Class methods:' do
|
|
|
|
describe '.attribute_to_references_hash_objects' do
|
|
|
|
it 'returns classes with conditions' do
|
|
|
|
expect(described_class.attribute_to_references_hash_objects).to match_array [Trigger, Overview, Job, Sla, Report::Profile ]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2022-01-18 14:37:16 +00:00
|
|
|
|
|
|
|
describe '#data_option_validations' do
|
|
|
|
context 'when maxlength is checked for non-integers' do
|
|
|
|
shared_examples 'tests the exception on invalid maxlength values' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { maxlength: 'brbrbr' }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{Data option must have integer for :maxlength})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on invalid maxlength values', 'input'
|
|
|
|
include_examples 'tests the exception on invalid maxlength values', 'textarea'
|
|
|
|
include_examples 'tests the exception on invalid maxlength values', 'richtext'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when type is checked' do
|
|
|
|
shared_examples 'tests the exception on invalid types' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { type: 'brbrbr' }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have one of text/password/tel/fax/email/url for :type})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on invalid types', 'input'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when min max values are checked' do
|
|
|
|
shared_examples 'tests the exception on invalid min max values' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
context 'when no integer for min' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { min: 'brbrbr' }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have integer for :min})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when no integer for max' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { max: 'brbrbr' }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have integer for :max})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when high integer for min' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { min: 999_999_999_999 }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{min must be lower than 2147483648})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when high integer for max' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { max: 999_999_999_999 }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{max must be lower than 2147483648})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when negative high integer for min' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { min: -999_999_999_999 }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{min must be higher than -2147483648})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when negative high integer for max' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { max: -999_999_999_999 }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{max must be higher than -2147483648})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when min is greater than max' do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: { min: 5, max: 2 }) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{min must be lower than max})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on invalid min max values', 'integer'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when default is checked' do
|
|
|
|
shared_examples 'tests the exception on missing default' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: {}) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have value for :default})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on missing default', 'select'
|
|
|
|
include_examples 'tests the exception on missing default', 'tree_select'
|
|
|
|
include_examples 'tests the exception on missing default', 'checkbox'
|
|
|
|
include_examples 'tests the exception on missing default', 'boolean'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when relation is checked' do
|
|
|
|
shared_examples 'tests the exception on missing relation' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: {}) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have non-nil value for either :options or :relation})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on missing relation', 'select'
|
|
|
|
include_examples 'tests the exception on missing relation', 'tree_select'
|
|
|
|
include_examples 'tests the exception on missing relation', 'checkbox'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when nil options are checked' do
|
|
|
|
shared_examples 'tests the exception on missing nil options' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: {}) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have non-nil value for :options})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on missing nil options', 'boolean'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when future is checked' do
|
|
|
|
shared_examples 'tests the exception on missing future' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: {}) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have boolean value for :future})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on missing future', 'datetime'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when past is checked' do
|
|
|
|
shared_examples 'tests the exception on missing past' do |type|
|
|
|
|
context "when type '#{type}'" do
|
|
|
|
subject(:attr) { described_class.new(data_type: type, data_option: {}) }
|
|
|
|
|
|
|
|
it 'does throw an exception' do
|
|
|
|
expect { attr.save! }.to raise_error(ActiveRecord::RecordInvalid, %r{must have boolean value for :past})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples 'tests the exception on missing past', 'datetime'
|
|
|
|
end
|
|
|
|
end
|
2018-08-24 01:39:17 +00:00
|
|
|
end
|