summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorAndreas Brandl <abrandl@gitlab.com>2018-03-06 20:09:01 +0100
committerAndreas Brandl <abrandl@gitlab.com>2018-03-16 13:35:25 +0100
commit754272e392c0da088200a1b56156600973f63267 (patch)
tree21fdb2f633deff884d39d89f7672f230f1d6c143 /spec
parenta0abb904782970de456dae5539ad5de2afef0e05 (diff)
downloadgitlab-ce-754272e392c0da088200a1b56156600973f63267.tar.gz
Atomic generation of internal ids for issues.
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/internal_ids.rb6
-rw-r--r--spec/models/internal_id_spec.rb87
2 files changed, 93 insertions, 0 deletions
diff --git a/spec/factories/internal_ids.rb b/spec/factories/internal_ids.rb
new file mode 100644
index 00000000000..b4c14d22a29
--- /dev/null
+++ b/spec/factories/internal_ids.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :internal_id do
+ project
+ usage { InternalId.usages.keys.first }
+ end
+end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
new file mode 100644
index 00000000000..b953b6a2df8
--- /dev/null
+++ b/spec/models/internal_id_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+
+describe InternalId do
+ let(:project) { create(:project) }
+ let(:usage) { :issues }
+
+ context 'validations' do
+ it { is_expected.to validate_presence_of(:usage) }
+ it { is_expected.to validate_presence_of(:project_id) }
+ end
+
+ describe '.generate_next' do
+ context 'in the absence of a record' do
+ subject { described_class.generate_next(project, usage) }
+
+ it 'creates a record if not yet present' do
+ expect { subject }.to change { described_class.count }.from(0).to(1)
+ end
+
+ it 'stores record attributes' do
+ subject
+
+ described_class.first.tap do |record|
+ expect(record.project).to eq(project)
+ expect(record.usage).to eq(usage.to_s) # TODO
+ end
+ end
+
+ context 'with existing issues' do
+ before do
+ rand(10).times { create(:issue, project: project) }
+ end
+
+ it 'calculates last_value values automatically' do
+ expect(subject).to eq(project.issues.size + 1)
+ end
+ end
+ end
+
+ it 'generates a strictly monotone, gapless sequence' do
+ seq = (0..rand(1000)).map do
+ described_class.generate_next(project, usage)
+ end
+ normalized = seq.map { |i| i - seq.min }
+ expect(normalized).to eq((0..seq.size - 1).to_a)
+ end
+ end
+
+ describe '#increment_and_save!' do
+ let(:id) { create(:internal_id) }
+ subject { id.increment_and_save! }
+
+ it 'returns incremented iid' do
+ value = id.last_value
+ expect(subject).to eq(value + 1)
+ end
+
+ it 'saves the record' do
+ subject
+ expect(id.changed?).to be_falsey
+ end
+
+ context 'with last_value=nil' do
+ let(:id) { build(:internal_id, last_value: nil) }
+
+ it 'returns 1' do
+ expect(subject).to eq(1)
+ end
+ end
+ end
+
+ describe '#calculate_last_value! (for issues)' do
+ subject do
+ build(:internal_id, project: project, usage: :issues)
+ end
+
+ context 'with existing issues' do
+ before do
+ rand(10).times { create(:issue, project: project) }
+ end
+
+ it 'counts related issues and saves' do
+ expect { subject.calculate_last_value! }.to change { subject.last_value }.from(nil).to(project.issues.size)
+ end
+ end
+ end
+end