project_spec.rb 19.1 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
Atsushi Ishida's avatar
Atsushi Ishida committed
24
#  avatar                 :string(255)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
25
#  import_status          :string(255)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
26 27
#  repository_size        :float            default(0.0)
#  star_count             :integer          default(0), not null
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
28 29
#  import_type            :string(255)
#  import_source          :string(255)
Atsushi Ishida's avatar
Atsushi Ishida committed
30
#  commit_count           :integer          default(0)
Stan Hu's avatar
Stan Hu committed
31
#  import_error           :text
32 33
#

gitlabhq's avatar
gitlabhq committed
34 35
require 'spec_helper'

Douwe Maan's avatar
Douwe Maan committed
36
describe Project, models: true do
37
  describe 'associations' do
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    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) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
57
    it { is_expected.to have_many(:commit_statuses) }
58 59 60 61 62 63
    it { is_expected.to have_many(:ci_commits) }
    it { is_expected.to have_many(:builds) }
    it { is_expected.to have_many(:runner_projects) }
    it { is_expected.to have_many(:runners) }
    it { is_expected.to have_many(:variables) }
    it { is_expected.to have_many(:triggers) }
gitlabhq's avatar
gitlabhq committed
64 65
  end

66 67 68 69 70 71 72 73
  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) }
74 75
  end

76
  describe 'validation' do
77 78
    let!(:project) { create(:project) }

79 80
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
81
    it { is_expected.to validate_length_of(:name).is_within(0..255) }
82

83 84
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
85 86
    it { is_expected.to validate_length_of(:path).is_within(0..255) }
    it { is_expected.to validate_length_of(:description).is_within(0..2000) }
87
    it { is_expected.to validate_presence_of(:creator) }
88
    it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
89
    it { is_expected.to validate_presence_of(:namespace) }
90

91
    it 'should not allow new projects beyond user limits' do
92
      project2 = build(:project)
93 94 95
      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/)
96
    end
gitlabhq's avatar
gitlabhq committed
97
  end
98 99 100
  
  describe 'project token' do
    it 'should set an random token if none provided' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
101 102
      project = FactoryGirl.create :empty_project, runners_token: ''
      expect(project.runners_token).not_to eq('')
103 104 105
    end

    it 'should not set an random toke if one provided' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
106 107
      project = FactoryGirl.create :empty_project, runners_token: 'my-token'
      expect(project.runners_token).to eq('my-token')
108 109
    end
  end
gitlabhq's avatar
gitlabhq committed
110

111
  describe 'Respond to' do
112 113 114 115 116 117 118
    it { is_expected.to respond_to(:url_to_repo) }
    it { is_expected.to respond_to(:repo_exists?) }
    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
119 120
  end

121 122 123 124 125 126 127 128
  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

129 130
  it 'should return valid url to repo' do
    project = Project.new(path: 'somewhere')
131
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
gitlabhq's avatar
gitlabhq committed
132 133
  end

Douwe Maan's avatar
Douwe Maan committed
134 135 136 137 138 139
  describe "#web_url" do
    let(:project) { create(:empty_project, path: "somewhere") }

    it 'returns the full web URL for this repo' do
      expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.path}/somewhere")
    end
140 141
  end

Douwe Maan's avatar
Douwe Maan committed
142 143 144 145 146 147
  describe "#web_url_without_protocol" do
    let(:project) { create(:empty_project, path: "somewhere") }

    it 'returns the web URL without the protocol for this repo' do
      expect(project.web_url_without_protocol).to eq("#{Gitlab.config.gitlab.url.split('://')[1]}/#{project.namespace.path}/somewhere")
    end
148 149
  end

150
  describe 'last_activity methods' do
151
    let(:project) { create(:project) }
152
    let(:last_event) { double(created_at: Time.now) }
gitlabhq's avatar
gitlabhq committed
153

154 155
    describe 'last_activity' do
      it 'should alias last_activity to last_event' do
156
        allow(project).to receive(:last_event).and_return(last_event)
157
        expect(project.last_activity).to eq(last_event)
158
      end
gitlabhq's avatar
gitlabhq committed
159 160
    end

161 162
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
163
        create(:event, project: project)
164
        expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i)
165
      end
166

167
      it 'returns the project\'s last update date if it has no events' do
168
        expect(project.last_activity_date).to eq(project.updated_at)
169
      end
170 171
    end
  end
172

173 174
  describe '#get_issue' do
    let(:project) { create(:empty_project) }
175
    let!(:issue)  { create(:issue, project: project) }
176 177 178 179 180 181

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

182 183 184 185
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      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 216 217 218
      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

219
  describe :update_merge_requests do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
220
    let(:project) { create(:project) }
221 222 223 224
    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 }
225

226
    it 'should close merge request if last commit from source branch was pushed to target branch' do
227 228
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
      merge_request.reload
229
      expect(merge_request.merged?).to be_truthy
230 231
    end

232
    it 'should update merge request commits with new one if pushed to source branch' do
233 234
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
      merge_request.reload
235
      expect(merge_request.last_commit.id).to eq(commit_id)
236 237
    end
  end
238 239 240 241 242

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

246
      it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) }
247
      it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) }
248
      it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
249 250 251 252 253 254 255
    end
  end

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

Vinnie Okada's avatar
Vinnie Okada committed
259
      it { expect(@project.to_param).to eq('gitlabhq') }
260 261
    end
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
262

263
  describe :repository do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
264 265
    let(:project) { create(:project) }

266
    it 'should return valid repo' do
267
      expect(project.repository).to be_kind_of(Repository)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
268 269
    end
  end
270

271
  describe :default_issues_tracker? do
272 273 274 275
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

    it "should be true if used internal tracker" do
276
      expect(project.default_issues_tracker?).to be_truthy
277 278 279
    end

    it "should be false if used other tracker" do
280
      expect(ext_project.default_issues_tracker?).to be_falsey
281 282 283
    end
  end

Andrew8xx8's avatar
Andrew8xx8 committed
284 285 286 287
  describe :can_have_issues_tracker_id? do
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

288
    it 'should be true for projects with external issues tracker if issues enabled' do
289
      expect(ext_project.can_have_issues_tracker_id?).to be_truthy
290
    end
Andrew8xx8's avatar
Andrew8xx8 committed
291

292
    it 'should be false for projects with internal issue tracker if issues enabled' do
293
      expect(project.can_have_issues_tracker_id?).to be_falsey
Andrew8xx8's avatar
Andrew8xx8 committed
294 295
    end

296
    it 'should be always false if issues disabled' do
Andrew8xx8's avatar
Andrew8xx8 committed
297 298 299
      project.issues_enabled = false
      ext_project.issues_enabled = false

300 301
      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
302 303
    end
  end
304 305

  describe :open_branches do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
306
    let(:project) { create(:project) }
307 308 309 310 311

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

312 313
    it { expect(project.open_branches.map(&:name)).to include('feature') }
    it { expect(project.open_branches.map(&:name)).not_to include('master') }
314
  end
Ciro Santilli's avatar
Ciro Santilli committed
315

316 317
  describe '#star_count' do
    it 'counts stars from multiple users' do
Ciro Santilli's avatar
Ciro Santilli committed
318 319 320 321 322
      user1 = create :user
      user2 = create :user
      project = create :project, :public

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

Ciro Santilli's avatar
Ciro Santilli committed
324
      user1.toggle_star(project)
325 326
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
327
      user2.toggle_star(project)
328 329 330
      project.reload
      expect(project.reload.star_count).to eq(2)

Ciro Santilli's avatar
Ciro Santilli committed
331
      user1.toggle_star(project)
332 333 334
      project.reload
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
335
      user2.toggle_star(project)
336 337 338 339
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

340
    it 'counts stars on the right project' do
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
      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
371 372
    end
  end
373 374 375 376 377 378

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

    it 'should be true if avatar is image' do
      project.update_attribute(:avatar, 'uploads/avatar.png')
379
      expect(project.avatar_type).to be_truthy
380 381 382 383
    end

    it 'should be false if avatar is html page' do
      project.update_attribute(:avatar, 'uploads/avatar.html')
384
      expect(project.avatar_type).to eq(['only images allowed'])
385 386
    end
  end
sue445's avatar
sue445 committed
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

  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
418 419 420

  describe :ci_commit do
    let(:project) { create :project }
421
    let(:commit) { create :ci_commit, project: project }
422 423 424

    it { expect(project.ci_commit(commit.sha)).to eq(commit) }
  end
425

426
  describe :builds_enabled do
427 428
    let(:project) { create :project }

429
    before { project.builds_enabled = true }
430

431 432 433
    subject { project.builds_enabled }

    it { expect(project.builds_enabled?).to be_truthy }
434
  end
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463

  describe '.trending' do
    let(:group)    { create(:group) }
    let(:project1) { create(:empty_project, :public, group: group) }
    let(:project2) { create(:empty_project, :public, group: group) }

    before do
      2.times do
        create(:note_on_commit, project: project1)
      end

      create(:note_on_commit, project: project2)
    end

    describe 'without an explicit start date' do
      subject { described_class.trending.to_a }

      it 'sorts Projects by the amount of notes in descending order' do
        expect(subject).to eq([project1, project2])
      end
    end

    describe 'with an explicit start date' do
      let(:date) { 2.months.ago }

      subject { described_class.trending(date).to_a }

      before do
        2.times do
464 465 466
          # Little fix for special issue related to Fractional Seconds support for MySQL.
          # See: https://github.com/rails/rails/pull/14359/files
          create(:note_on_commit, project: project2, created_at: date + 1)
467 468 469 470 471 472 473 474
        end
      end

      it 'sorts Projects by the amount of notes in descending order' do
        expect(subject).to eq([project2, project1])
      end
    end
  end
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

  describe '.visible_to_user' do
    let!(:project) { create(:project, :private) }
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
        project.team.add_user(user, Gitlab::Access::MASTER)
      end

      it { is_expected.to eq([project]) }
    end

    describe 'when a user does not have access to any projects' do
      it { is_expected.to eq([]) }
    end
  end
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525

  context 'shared runners by default' do
    let(:project) { create(:empty_project) }

    subject { project.shared_runners_enabled }

    context 'are enabled' do
      before { stub_application_setting(shared_runners_enabled: true) }

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
      before { stub_application_setting(shared_runners_enabled: false) }

      it { is_expected.to be_falsey }
    end
  end

  describe :any_runners do
    let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) }
    let(:specific_runner) { create(:ci_specific_runner) }
    let(:shared_runner) { create(:ci_shared_runner) }

    context 'for shared runners disabled' do
      let(:shared_runners_enabled) { false }
      
      it 'there are no runners available' do
        expect(project.any_runners?).to be_falsey
      end
  
      it 'there is a specific runner' do
526
        project.runners << specific_runner
527 528 529 530 531 532 533 534 535
        expect(project.any_runners?).to be_truthy
      end
  
      it 'there is a shared runner, but they are prohibited to use' do
        shared_runner
        expect(project.any_runners?).to be_falsey
      end
  
      it 'checks the presence of specific runner' do
536
        project.runners << specific_runner
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
        expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
      end
    end
    
    context 'for shared runners enabled' do
      let(:shared_runners_enabled) { true }
      
      it 'there is a shared runner' do
        shared_runner
        expect(project.any_runners?).to be_truthy
      end

      it 'checks the presence of shared runner' do
        shared_runner
        expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
      end
    end
  end
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578

  describe '#visibility_level_allowed?' do
    let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL }

    context 'when checking on non-forked project' do
      it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
      it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
      it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_truthy }
    end

    context 'when checking on forked project' do
      let(:forked_project) { create :forked_project_with_submodules }

      before do
        forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
        forked_project.save
      end

      it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
      it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
      it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
    end

  end
gitlabhq's avatar
gitlabhq committed
579
end