4ff75e3179
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
171 lines
4.4 KiB
Ruby
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
|