milestone_spec.rb 7.6 KB
Newer Older
D
Dmitriy Zaporozhets 已提交
1 2
require 'spec_helper'

D
Douwe Maan 已提交
3
describe Milestone, models: true do
4
  describe "Validation" do
5 6 7 8
    before do
      allow(subject).to receive(:set_iid).and_return(false)
    end

9 10
    it { is_expected.to validate_presence_of(:title) }
    it { is_expected.to validate_presence_of(:project) }
V
Valery Sizov 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24

    describe 'start_date' do
      it 'adds an error when start_date is greated then due_date' do
        milestone = build(:milestone, start_date: Date.tomorrow, due_date: Date.yesterday)

        expect(milestone).not_to be_valid
        expect(milestone.errors[:start_date]).to include("Can't be greater than due date")
      end
    end
  end

  describe "Associations" do
    it { is_expected.to belong_to(:project) }
    it { is_expected.to have_many(:issues) }
25 26
  end

27 28
  let(:milestone) { create(:milestone) }
  let(:issue) { create(:issue) }
29
  let(:user) { create(:user) }
30

31
  describe "#title" do
32
    let(:milestone) { create(:milestone, title: "<b>foo & bar -> 2.2</b>") }
33 34

    it "sanitizes title" do
35
      expect(milestone.title).to eq("foo & bar -> 2.2")
36 37 38
    end
  end

39
  describe "unique milestone title per project" do
40
    it "does not accept the same title in a project twice" do
41 42 43 44
      new_milestone = Milestone.new(project: milestone.project, title: milestone.title)
      expect(new_milestone).not_to be_valid
    end

45
    it "accepts the same title in another project" do
46 47 48 49 50 51 52
      project = build(:project)
      new_milestone = Milestone.new(project: project, title: milestone.title)

      expect(new_milestone).to be_valid
    end
  end

53
  describe "#percent_complete" do
54
    it "does not count open issues" do
55
      milestone.issues << issue
56
      expect(milestone.percent_complete(user)).to eq(0)
57 58
    end

59
    it "counts closed issues" do
A
Andrew8xx8 已提交
60
      issue.close
61
      milestone.issues << issue
62
      expect(milestone.percent_complete(user)).to eq(100)
63
    end
64

65
    it "recovers from dividing by zero" do
66
      expect(milestone.percent_complete(user)).to eq(0)
67 68 69
    end
  end

70
  describe '#expired?' do
71 72
    context "expired" do
      before do
73
        allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
74 75
      end

76
      it { expect(milestone.expired?).to be_truthy }
77 78 79 80
    end

    context "not expired" do
      before do
81
        allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
82 83
      end

84
      it { expect(milestone.expired?).to be_falsey }
85 86 87
    end
  end

V
Valery Sizov 已提交
88 89 90 91 92 93 94 95 96 97 98 99
  describe '#upcoming?' do
    it 'returns true' do
      milestone = build(:milestone, start_date: Time.now + 1.month)
      expect(milestone.upcoming?).to be_truthy
    end

    it 'returns false' do
      milestone = build(:milestone, start_date: Date.today.prev_year)
      expect(milestone.upcoming?).to be_falsey
    end
  end

100
  describe '#percent_complete' do
101
    before do
102
      allow(milestone).to receive_messages(
103 104 105 106 107
        closed_items_count: 3,
        total_items_count: 4
      )
    end

108
    it { expect(milestone.percent_complete(user)).to eq(75) }
109 110 111 112 113
  end

  describe :items_count do
    before do
      milestone.issues << create(:issue)
A
Andrew8xx8 已提交
114
      milestone.issues << create(:closed_issue)
115 116 117
      milestone.merge_requests << create(:merge_request)
    end

118 119 120
    it { expect(milestone.closed_items_count(user)).to eq(1) }
    it { expect(milestone.total_items_count(user)).to eq(3) }
    it { expect(milestone.is_empty?(user)).to be_falsey }
121 122
  end

123
  describe '#can_be_closed?' do
124
    it { expect(milestone.can_be_closed?).to be_truthy }
125
  end
A
Andrew8xx8 已提交
126

127
  describe '#total_items_count' do
128
    before do
129 130
      create :closed_issue, milestone: milestone
      create :merge_request, milestone: milestone
131
    end
A
Andrew8xx8 已提交
132

133
    it 'returns total count of issues and merge requests assigned to milestone' do
134
      expect(milestone.total_items_count(user)).to eq 2
A
Andrew8xx8 已提交
135 136 137
    end
  end

138
  describe '#can_be_closed?' do
139
    before do
A
Andrew8xx8 已提交
140
      milestone = create :milestone
141 142
      create :closed_issue, milestone: milestone

143
      create :issue
144
    end
A
Andrew8xx8 已提交
145

146
    it 'returns true if milestone active and all nested issues closed' do
147
      expect(milestone.can_be_closed?).to be_truthy
A
Andrew8xx8 已提交
148 149
    end

150
    it 'returns false if milestone active and not all nested issues closed' do
151
      issue.milestone = milestone
A
Andrey Kumanyaev 已提交
152
      issue.save
A
Andrew8xx8 已提交
153

154
      expect(milestone.can_be_closed?).to be_falsey
A
Andrew8xx8 已提交
155 156 157
    end
  end

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
  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
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

  describe '.search' do
    let(:milestone) { create(:milestone, title: 'foo', description: 'bar') }

    it 'returns milestones with a matching title' do
      expect(described_class.search(milestone.title)).to eq([milestone])
    end

    it 'returns milestones with a partially matching title' do
      expect(described_class.search(milestone.title[0..2])).to eq([milestone])
    end

    it 'returns milestones with a matching title regardless of the casing' do
      expect(described_class.search(milestone.title.upcase)).to eq([milestone])
    end

    it 'returns milestones with a matching description' do
      expect(described_class.search(milestone.description)).to eq([milestone])
    end

    it 'returns milestones with a partially matching description' do
      expect(described_class.search(milestone.description[0..2])).
        to eq([milestone])
    end

    it 'returns milestones with a matching description regardless of the casing' do
      expect(described_class.search(milestone.description.upcase)).
        to eq([milestone])
    end
  end
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

  describe '.upcoming_ids_by_projects' do
    let(:project_1) { create(:empty_project) }
    let(:project_2) { create(:empty_project) }
    let(:project_3) { create(:empty_project) }
    let(:projects) { [project_1, project_2, project_3] }

    let!(:past_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now - 1.day) }
    let!(:current_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 1.day) }
    let!(:future_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 2.days) }

    let!(:past_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now - 1.day) }
    let!(:closed_milestone_project_2) { create(:milestone, :closed, project: project_2, due_date: Time.now + 1.day) }
    let!(:current_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now + 2.days) }

    let!(:past_milestone_project_3) { create(:milestone, project: project_3, due_date: Time.now - 1.day) }

S
Sean McGivern 已提交
233 234 235
    # The call to `#try` is because this returns a relation with a Postgres DB,
    # and an array of IDs with a MySQL DB.
    let(:milestone_ids) { Milestone.upcoming_ids_by_projects(projects).map { |id| id.try(:id) || id } }
236 237 238 239 240 241 242 243 244 245 246 247 248

    it 'returns the next upcoming open milestone ID for each project' do
      expect(milestone_ids).to contain_exactly(current_milestone_project_1.id, current_milestone_project_2.id)
    end

    context 'when the projects have no open upcoming milestones' do
      let(:projects) { [project_3] }

      it 'returns no results' do
        expect(milestone_ids).to be_empty
      end
    end
  end
D
Dmitriy Zaporozhets 已提交
249
end