diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb index 6a0f29806c4..bba71eee8bd 100644 --- a/app/models/concerns/atomic_internal_id.rb +++ b/app/models/concerns/atomic_internal_id.rb @@ -26,7 +26,7 @@ module AtomicInternalId included do class << self - def has_internal_id(on, scope:, usage: nil, init: nil) # rubocop:disable Naming/PredicateName + def has_internal_id(on, scope:, usage: nil, init:) # rubocop:disable Naming/PredicateName before_validation(on: :create) do if self.public_send(on).blank? # rubocop:disable GitlabSecurity/PublicSend diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index feed7968f09..11154291368 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -9,11 +9,17 @@ describe Issue do describe 'modules' do subject { described_class } - it { is_expected.to include_module(InternalId) } it { is_expected.to include_module(Issuable) } it { is_expected.to include_module(Referable) } it { is_expected.to include_module(Sortable) } it { is_expected.to include_module(Taskable) } + + it_behaves_like 'AtomicInternalId' do + let(:internal_id_attribute) { :iid } + let(:instance) { build(:issue) } + let(:scope_attrs) { { project: instance.project } } + let(:usage) { :issues } + end end subject { create(:issue) } diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb new file mode 100644 index 00000000000..671aa314bd6 --- /dev/null +++ b/spec/support/shared_examples/models/atomic_internal_id_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +shared_examples_for 'AtomicInternalId' do + describe '.has_internal_id' do + describe 'Module inclusion' do + subject { described_class } + + it { is_expected.to include_module(AtomicInternalId) } + end + + describe 'Validation' do + subject { instance } + + before do + allow(InternalId).to receive(:generate_next).and_return(nil) + end + + it { is_expected.to validate_presence_of(internal_id_attribute) } + it { is_expected.to validate_numericality_of(internal_id_attribute) } + end + + describe 'internal id generation' do + subject { instance.save! } + + it 'calls InternalId.generate_next and sets internal id attribute' do + iid = rand(1..1000) + expect(InternalId).to receive(:generate_next).with(instance, scope_attrs, usage, any_args).and_return(iid) + subject + expect(instance.public_send(internal_id_attribute)).to eq(iid) # rubocop:disable GitlabSecurity/PublicSend + end + + it 'does not overwrite an existing internal id' do + instance.public_send("#{internal_id_attribute}=", 4711) # rubocop:disable GitlabSecurity/PublicSend + expect { subject }.not_to change { instance.public_send(internal_id_attribute) } # rubocop:disable GitlabSecurity/PublicSend + end + end + end +end