Read about our upcoming Code of Conduct on this issue

project_spec.rb 13.3 KB
Newer Older
1
2
3
4
# == Schema Information
#
# Table name: projects
#
5
#  id                     :integer          not null, primary key
6
7
8
#  name                   :string(255)
#  path                   :string(255)
#  description            :text
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
9
10
#  created_at             :datetime
#  updated_at             :datetime
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
11
#  creator_id             :integer
12
13
14
15
#  issues_enabled         :boolean          default(TRUE), not null
#  wall_enabled           :boolean          default(TRUE), not null
#  merge_requests_enabled :boolean          default(TRUE), not null
#  wiki_enabled           :boolean          default(TRUE), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
16
#  namespace_id           :integer
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
17
#  issues_tracker         :string(255)      default("gitlab"), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
18
#  issues_tracker_id      :string(255)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
19
#  snippets_enabled       :boolean          default(TRUE), not null
20
#  last_activity_at       :datetime
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
21
#  import_url             :string(255)
22
#  visibility_level       :integer          default(0), not null
23
#  archived               :boolean          default(FALSE), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
24
#  import_status          :string(255)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
25
26
#  repository_size        :float            default(0.0)
#  star_count             :integer          default(0), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
27
28
#  import_type            :string(255)
#  import_source          :string(255)
29
#  avatar                 :string(255)
30
31
#

gitlabhq's avatar
gitlabhq committed
32
33
34
require 'spec_helper'

describe Project do
35
  describe 'associations' do
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    it { is_expected.to belong_to(:group) }
    it { is_expected.to belong_to(:namespace) }
    it { is_expected.to belong_to(:creator).class_name('User') }
    it { is_expected.to have_many(:users) }
    it { is_expected.to have_many(:events).dependent(:destroy) }
    it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
    it { is_expected.to have_many(:issues).dependent(:destroy) }
    it { is_expected.to have_many(:milestones).dependent(:destroy) }
    it { is_expected.to have_many(:project_members).dependent(:destroy) }
    it { is_expected.to have_many(:notes).dependent(:destroy) }
    it { is_expected.to have_many(:snippets).class_name('ProjectSnippet').dependent(:destroy) }
    it { is_expected.to have_many(:deploy_keys_projects).dependent(:destroy) }
    it { is_expected.to have_many(:deploy_keys) }
    it { is_expected.to have_many(:hooks).dependent(:destroy) }
    it { is_expected.to have_many(:protected_branches).dependent(:destroy) }
    it { is_expected.to have_one(:forked_project_link).dependent(:destroy) }
    it { is_expected.to have_one(:slack_service).dependent(:destroy) }
    it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
    it { is_expected.to have_one(:asana_service).dependent(:destroy) }
gitlabhq's avatar
gitlabhq committed
55
56
  end

57
58
59
60
61
62
63
64
  describe 'modules' do
    subject { described_class }

    it { is_expected.to include_module(Gitlab::ConfigHelper) }
    it { is_expected.to include_module(Gitlab::ShellAdapter) }
    it { is_expected.to include_module(Gitlab::VisibilityLevel) }
    it { is_expected.to include_module(Referable) }
    it { is_expected.to include_module(Sortable) }
65
66
  end

67
  describe 'validation' do
68
69
    let!(:project) { create(:project) }

70
71
72
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
    it { is_expected.to ensure_length_of(:name).is_within(0..255) }
73

74
75
76
77
78
79
80
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
    it { is_expected.to ensure_length_of(:path).is_within(0..255) }
    it { is_expected.to ensure_length_of(:description).is_within(0..2000) }
    it { is_expected.to validate_presence_of(:creator) }
    it { is_expected.to ensure_length_of(:issues_tracker_id).is_within(0..255) }
    it { is_expected.to validate_presence_of(:namespace) }
81

82
    it 'should not allow new projects beyond user limits' do
83
      project2 = build(:project)
84
85
86
      allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
      expect(project2).not_to be_valid
      expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/)
87
    end
gitlabhq's avatar
gitlabhq committed
88
89
  end

90
  describe 'Respond to' do
91
92
93
94
95
96
97
98
    it { is_expected.to respond_to(:url_to_repo) }
    it { is_expected.to respond_to(:repo_exists?) }
    it { is_expected.to respond_to(:satellite) }
    it { is_expected.to respond_to(:update_merge_requests) }
    it { is_expected.to respond_to(:execute_hooks) }
    it { is_expected.to respond_to(:name_with_namespace) }
    it { is_expected.to respond_to(:owner) }
    it { is_expected.to respond_to(:path_with_namespace) }
gitlabhq's avatar
gitlabhq committed
99
100
  end

101
102
103
104
105
106
107
108
  describe '#to_reference' do
    let(:project) { create(:empty_project) }

    it 'returns a String reference to the object' do
      expect(project.to_reference).to eq project.path_with_namespace
    end
  end

109
110
  it 'should return valid url to repo' do
    project = Project.new(path: 'somewhere')
111
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
gitlabhq's avatar
gitlabhq committed
112
113
  end

114
115
  it 'returns the full web URL for this repo' do
    project = Project.new(path: 'somewhere')
116
    expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/somewhere")
117
118
  end

119
120
  it 'returns the web URL without the protocol for this repo' do
    project = Project.new(path: 'somewhere')
121
    expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/somewhere")
122
123
  end

124
  describe 'last_activity methods' do
125
    let(:project) { create(:project) }
126
    let(:last_event) { double(created_at: Time.now) }
gitlabhq's avatar
gitlabhq committed
127

128
129
    describe 'last_activity' do
      it 'should alias last_activity to last_event' do
130
        project.stub(last_event: last_event)
131
        expect(project.last_activity).to eq(last_event)
132
      end
gitlabhq's avatar
gitlabhq committed
133
134
    end

135
136
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
Andrey Kumanyaev's avatar
Andrey Kumanyaev committed
137
        last_activity_event = create(:event, project: project)
138
        expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i)
139
      end
140

141
      it 'returns the project\'s last update date if it has no events' do
142
        expect(project.last_activity_date).to eq(project.updated_at)
143
      end
144
145
    end
  end
146

147
148
149
150
151
152
153
154
155
156
157
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
186
187
188
  describe '#get_issue' do
    let(:project) { create(:empty_project) }
    let(:issue)   { create(:issue, project: project) }

    context 'with default issues tracker' do
      it 'returns an issue' do
        expect(project.get_issue(issue.iid)).to eq issue
      end

      it 'returns nil when no issue found' do
        expect(project.get_issue(999)).to be_nil
      end
    end

    context 'with external issues tracker' do
      before do
        allow(project).to receive(:default_issues_tracker?).and_return(false)
      end

      it 'returns an ExternalIssue' do
        issue = project.get_issue('FOO-1234')
        expect(issue).to be_kind_of(ExternalIssue)
        expect(issue.iid).to eq 'FOO-1234'
        expect(issue.project).to eq project
      end
    end
  end

  describe '#issue_exists?' do
    let(:project) { create(:empty_project) }

    it 'is truthy when issue exists' do
      expect(project).to receive(:get_issue).and_return(double)
      expect(project.issue_exists?(1)).to be_truthy
    end

    it 'is falsey when issue does not exist' do
      expect(project).to receive(:get_issue).and_return(nil)
      expect(project.issue_exists?(1)).to be_falsey
    end
  end

189
  describe :update_merge_requests do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
190
    let(:project) { create(:project) }
191
192
193
194
    let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
    let(:key) { create(:key, user_id: project.owner.id) }
    let(:prev_commit_id) { merge_request.commits.last.id }
    let(:commit_id) { merge_request.commits.first.id }
195

196
    it 'should close merge request if last commit from source branch was pushed to target branch' do
197
198
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
      merge_request.reload
199
      expect(merge_request.merged?).to be_truthy
200
201
    end

202
    it 'should update merge request commits with new one if pushed to source branch' do
203
204
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
      merge_request.reload
205
      expect(merge_request.last_commit.id).to eq(commit_id)
206
207
    end
  end
208
209
210
211
212

  describe :find_with_namespace do
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
213
        @project = create(:project, name: 'gitlabhq', namespace: @group)
214
215
      end

216
217
      it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) }
      it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
218
219
220
221
222
223
224
    end
  end

  describe :to_param do
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
225
        @project = create(:project, name: 'gitlabhq', namespace: @group)
226
227
      end

Vinnie Okada's avatar
Vinnie Okada committed
228
      it { expect(@project.to_param).to eq('gitlabhq') }
229
230
    end
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
231

232
  describe :repository do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
233
234
    let(:project) { create(:project) }

235
    it 'should return valid repo' do
236
      expect(project.repository).to be_kind_of(Repository)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
237
238
    end
  end
239

240
  describe :default_issues_tracker? do
241
242
243
244
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

    it "should be true if used internal tracker" do
245
      expect(project.default_issues_tracker?).to be_truthy
246
247
248
    end

    it "should be false if used other tracker" do
249
      expect(ext_project.default_issues_tracker?).to be_falsey
250
251
252
    end
  end

Andrew8xx8's avatar
Andrew8xx8 committed
253
254
255
256
  describe :can_have_issues_tracker_id? do
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

257
    it 'should be true for projects with external issues tracker if issues enabled' do
258
      expect(ext_project.can_have_issues_tracker_id?).to be_truthy
259
    end
Andrew8xx8's avatar
Andrew8xx8 committed
260

261
    it 'should be false for projects with internal issue tracker if issues enabled' do
262
      expect(project.can_have_issues_tracker_id?).to be_falsey
Andrew8xx8's avatar
Andrew8xx8 committed
263
264
    end

265
    it 'should be always false if issues disabled' do
Andrew8xx8's avatar
Andrew8xx8 committed
266
267
268
      project.issues_enabled = false
      ext_project.issues_enabled = false

269
270
      expect(project.can_have_issues_tracker_id?).to be_falsey
      expect(ext_project.can_have_issues_tracker_id?).to be_falsey
Andrew8xx8's avatar
Andrew8xx8 committed
271
272
    end
  end
273
274

  describe :open_branches do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
275
    let(:project) { create(:project) }
276
277
278
279
280

    before do
      project.protected_branches.create(name: 'master')
    end

281
282
    it { expect(project.open_branches.map(&:name)).to include('feature') }
    it { expect(project.open_branches.map(&:name)).not_to include('master') }
283
  end
Ciro Santilli's avatar
Ciro Santilli committed
284

285
286
  describe '#star_count' do
    it 'counts stars from multiple users' do
Ciro Santilli's avatar
Ciro Santilli committed
287
288
289
290
291
      user1 = create :user
      user2 = create :user
      project = create :project, :public

      expect(project.star_count).to eq(0)
292

Ciro Santilli's avatar
Ciro Santilli committed
293
      user1.toggle_star(project)
294
295
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
296
      user2.toggle_star(project)
297
298
299
      project.reload
      expect(project.reload.star_count).to eq(2)

Ciro Santilli's avatar
Ciro Santilli committed
300
      user1.toggle_star(project)
301
302
303
      project.reload
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
304
      user2.toggle_star(project)
305
306
307
308
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

309
    it 'counts stars on the right project' do
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
      user = create :user
      project1 = create :project, :public
      project2 = create :project, :public

      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(0)

      user.toggle_star(project1)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(1)
      expect(project2.star_count).to eq(0)

      user.toggle_star(project1)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(0)

      user.toggle_star(project2)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(1)

      user.toggle_star(project2)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(0)
Ciro Santilli's avatar
Ciro Santilli committed
340
    end
341
342
343
344
345
346
347
348
349
350
351

    it 'is decremented when an upvoter account is deleted' do
      user = create :user
      project = create :project, :public
      user.toggle_star(project)
      project.reload
      expect(project.star_count).to eq(1)
      user.destroy
      project.reload
      expect(project.star_count).to eq(0)
    end
Ciro Santilli's avatar
Ciro Santilli committed
352
  end
353
354
355
356
357
358

  describe :avatar_type do
    let(:project) { create(:project) }

    it 'should be true if avatar is image' do
      project.update_attribute(:avatar, 'uploads/avatar.png')
359
      expect(project.avatar_type).to be_truthy
360
361
362
363
    end

    it 'should be false if avatar is html page' do
      project.update_attribute(:avatar, 'uploads/avatar.html')
364
      expect(project.avatar_type).to eq(['only images allowed'])
365
366
    end
  end
sue445's avatar
sue445 committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

  describe :avatar_url do
    subject { project.avatar_url }

    let(:project) { create(:project) }

    context 'When avatar file is uploaded' do
      before do
        project.update_columns(avatar: 'uploads/avatar.png')
        allow(project.avatar).to receive(:present?) { true }
      end

      let(:avatar_path) do
        "/uploads/project/avatar/#{project.id}/uploads/avatar.png"
      end

      it { should eq "http://localhost#{avatar_path}" }
    end

    context 'When avatar file in git' do
      before do
        allow(project).to receive(:avatar_in_git) { true }
      end

      let(:avatar_path) do
        "/#{project.namespace.name}/#{project.path}/avatar"
      end

      it { should eq "http://localhost#{avatar_path}" }
    end
  end
gitlabhq's avatar
gitlabhq committed
398
end