gitlab-org--gitlab-foss/spec/models/milestone_spec.rb
Yorick Peterse 4ff75e3179 Improve performance of sorting milestone issues
This cuts down the time it takes to sort issues of a milestone by about
10x. In the previous setup the code would run a SQL query for every
issue that had to be sorted. The new setup instead runs a single SQL
query to update all the given issues at once.

The attached benchmark used to run at around 60 iterations per second,
using the new setup this hovers around 600 iterations per second. Timing
wise a request to update a milestone with 40-something issues would take
about 760 ms, in the new setup this only takes about 130 ms.

Fixes #3066
2015-10-19 11:37:14 +02:00

171 lines
4.4 KiB
Ruby

# == Schema Information
#
# Table name: milestones
#
# id :integer not null, primary key
# title :string(255) not null
# project_id :integer not null
# description :text
# due_date :date
# created_at :datetime
# updated_at :datetime
# state :string(255)
# iid :integer
#
require 'spec_helper'
describe Milestone do
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to have_many(:issues) }
end
describe "Validation" do
before do
allow(subject).to receive(:set_iid).and_return(false)
end
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:project) }
end
let(:milestone) { create(:milestone) }
let(:issue) { create(:issue) }
describe "#percent_complete" do
it "should not count open issues" do
milestone.issues << issue
expect(milestone.percent_complete).to eq(0)
end
it "should count closed issues" do
issue.close
milestone.issues << issue
expect(milestone.percent_complete).to eq(100)
end
it "should recover from dividing by zero" do
expect(milestone.issues).to receive(:count).and_return(0)
expect(milestone.percent_complete).to eq(0)
end
end
describe "#expires_at" do
it "should be nil when due_date is unset" do
milestone.update_attributes(due_date: nil)
expect(milestone.expires_at).to be_nil
end
it "should not be nil when due_date is set" do
milestone.update_attributes(due_date: Date.tomorrow)
expect(milestone.expires_at).to be_present
end
end
describe :expired? do
context "expired" do
before do
allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
end
it { expect(milestone.expired?).to be_truthy }
end
context "not expired" do
before do
allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
end
it { expect(milestone.expired?).to be_falsey }
end
end
describe :percent_complete do
before do
allow(milestone).to receive_messages(
closed_items_count: 3,
total_items_count: 4
)
end
it { expect(milestone.percent_complete).to eq(75) }
end
describe :items_count do
before do
milestone.issues << create(:issue)
milestone.issues << create(:closed_issue)
milestone.merge_requests << create(:merge_request)
end
it { expect(milestone.closed_items_count).to eq(1) }
it { expect(milestone.open_items_count).to eq(2) }
it { expect(milestone.total_items_count).to eq(3) }
it { expect(milestone.is_empty?).to be_falsey }
end
describe :can_be_closed? do
it { expect(milestone.can_be_closed?).to be_truthy }
end
describe :is_empty? do
before do
create :closed_issue, milestone: milestone
create :merge_request, milestone: milestone
end
it 'Should return total count of issues and merge requests assigned to milestone' do
expect(milestone.total_items_count).to eq 2
end
end
describe :can_be_closed? do
before do
milestone = create :milestone
create :closed_issue, milestone: milestone
create :issue
end
it 'should be true if milestone active and all nested issues closed' do
expect(milestone.can_be_closed?).to be_truthy
end
it 'should be false if milestone active and not all nested issues closed' do
issue.milestone = milestone
issue.save
expect(milestone.can_be_closed?).to be_falsey
end
end
describe '#sort_issues' do
let(:milestone) { create(:milestone) }
let(:issue1) { create(:issue, milestone: milestone, position: 1) }
let(:issue2) { create(:issue, milestone: milestone, position: 2) }
let(:issue3) { create(:issue, milestone: milestone, position: 3) }
let(:issue4) { create(:issue, position: 42) }
it 'sorts the given issues' do
milestone.sort_issues([issue3.id, issue2.id, issue1.id])
issue1.reload
issue2.reload
issue3.reload
expect(issue1.position).to eq(3)
expect(issue2.position).to eq(2)
expect(issue3.position).to eq(1)
end
it 'ignores issues not part of the milestone' do
milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id])
issue4.reload
expect(issue4.position).to eq(42)
end
end
end