BigW Consortium Gitlab

milestone_spec.rb 8.08 KB
Newer Older
1 2
require 'spec_helper'

Douwe Maan committed
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 11 12 13 14 15 16 17 18 19 20 21
    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) }
22 23
  end

24
  let(:project) { create(:empty_project, :public) }
25 26
  let(:milestone) { create(:milestone, project: project) }
  let(:issue) { create(:issue, project: project) }
27
  let(:user) { create(:user) }
28

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

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

Felipe Artur committed
37 38 39 40 41 42 43 44 45 46 47 48 49
  describe "unique milestone title" do
    context "per project" do
      it "does not accept the same title in a project twice" do
        new_milestone = Milestone.new(project: milestone.project, title: milestone.title)
        expect(new_milestone).not_to be_valid
      end

      it "accepts the same title in another project" do
        project = create(:empty_project)
        new_milestone = Milestone.new(project: project, title: milestone.title)

        expect(new_milestone).to be_valid
      end
50 51
    end

Felipe Artur committed
52 53 54 55 56 57 58 59 60 61 62 63 64
    context "per group" do
      let(:group) { create(:group) }
      let(:milestone) { create(:milestone, group: group) }

      before do
        project.update(group: group)
      end

      it "does not accept the same title in a group twice" do
        new_milestone = Milestone.new(group: group, title: milestone.title)

        expect(new_milestone).not_to be_valid
      end
65

Felipe Artur committed
66 67 68 69 70 71 72
      it "does not accept the same title of a child project milestone" do
        create(:milestone, project: group.projects.first)

        new_milestone = Milestone.new(group: group, title: milestone.title)

        expect(new_milestone).not_to be_valid
      end
73 74 75
    end
  end

76
  describe "#percent_complete" do
77
    it "does not count open issues" do
78
      milestone.issues << issue
79
      expect(milestone.percent_complete(user)).to eq(0)
80 81
    end

82
    it "counts closed issues" do
83
      issue.close
84
      milestone.issues << issue
85
      expect(milestone.percent_complete(user)).to eq(100)
86
    end
87

88
    it "recovers from dividing by zero" do
89
      expect(milestone.percent_complete(user)).to eq(0)
90 91 92
    end
  end

93
  describe '#expired?' do
94 95
    context "expired" do
      before do
96
        allow(milestone).to receive(:due_date).and_return(Date.today.prev_year)
97 98
      end

99
      it { expect(milestone.expired?).to be_truthy }
100 101 102 103
    end

    context "not expired" do
      before do
104
        allow(milestone).to receive(:due_date).and_return(Date.today.next_year)
105 106
      end

107
      it { expect(milestone.expired?).to be_falsey }
108 109 110
    end
  end

111 112 113 114 115 116 117 118 119 120 121 122
  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

123
  describe '#percent_complete' do
124
    before do
125
      allow(milestone).to receive_messages(
126 127 128 129 130
        closed_items_count: 3,
        total_items_count: 4
      )
    end

131
    it { expect(milestone.percent_complete(user)).to eq(75) }
132 133
  end

134
  describe '#can_be_closed?' do
135
    it { expect(milestone.can_be_closed?).to be_truthy }
136
  end
137

138
  describe '#total_items_count' do
139
    before do
140
      create :closed_issue, milestone: milestone, project: project
141
      create :merge_request, milestone: milestone
142
    end
143

144
    it 'returns total count of issues and merge requests assigned to milestone' do
145
      expect(milestone.total_items_count(user)).to eq 2
146 147 148
    end
  end

149
  describe '#can_be_closed?' do
150
    before do
151
      milestone = create :milestone
152 153
      create :closed_issue, milestone: milestone

154
      create :issue
155
    end
156

157
    it 'returns true if milestone active and all nested issues closed' do
158
      expect(milestone.can_be_closed?).to be_truthy
159 160
    end

161
    it 'returns false if milestone active and not all nested issues closed' do
162
      issue.milestone = milestone
163
      issue.save
164

165
      expect(milestone.can_be_closed?).to be_falsey
166 167 168
    end
  end

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
  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
189 190
      expect(described_class.search(milestone.description[0..2]))
        .to eq([milestone])
191 192 193
    end

    it 'returns milestones with a matching description regardless of the casing' do
194 195
      expect(described_class.search(milestone.description.upcase))
        .to eq([milestone])
196 197
    end
  end
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

  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) }

215 216 217
    # 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 } }
218 219 220 221 222 223 224 225 226 227 228 229 230

    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
231 232 233 234 235 236 237 238 239 240

  describe '#to_reference' do
    let(:project) { build(:empty_project, name: 'sample-project') }
    let(:milestone) { build(:milestone, iid: 1, project: project) }

    it 'returns a String reference to the object' do
      expect(milestone.to_reference).to eq "%1"
    end

    it 'supports a cross-project reference' do
241
      another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
242 243 244
      expect(milestone.to_reference(another_project)).to eq "sample-project%1"
    end
  end
245 246 247 248 249 250 251 252 253 254 255 256 257

  describe '#participants' do
    let(:project) { build(:empty_project, name: 'sample-project') }
    let(:milestone) { build(:milestone, iid: 1, project: project) }

    it 'returns participants without duplicates' do
      user = create :user
      create :issue, project: project, milestone: milestone, assignees: [user]
      create :issue, project: project, milestone: milestone, assignees: [user]

      expect(milestone.participants).to eq [user]
    end
  end
258
end