BigW Consortium Gitlab

project_spec.rb 19.4 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 committed
9 10
#  created_at             :datetime
#  updated_at             :datetime
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 committed
16
#  namespace_id           :integer
Dmitriy Zaporozhets committed
17
#  issues_tracker         :string(255)      default("gitlab"), not null
Dmitriy Zaporozhets committed
18
#  issues_tracker_id      :string(255)
Dmitriy Zaporozhets committed
19
#  snippets_enabled       :boolean          default(TRUE), not null
20
#  last_activity_at       :datetime
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 committed
24
#  avatar                 :string(255)
Dmitriy Zaporozhets committed
25
#  import_status          :string(255)
Dmitriy Zaporozhets committed
26 27
#  repository_size        :float            default(0.0)
#  star_count             :integer          default(0), not null
Dmitriy Zaporozhets committed
28 29
#  import_type            :string(255)
#  import_source          :string(255)
Atsushi Ishida committed
30
#  commit_count           :integer          default(0)
Stan Hu committed
31
#  import_error           :text
Stan Hu committed
32 33 34 35 36 37 38
#  ci_id                  :integer
#  builds_enabled         :boolean          default(TRUE), not null
#  shared_runners_enabled :boolean          default(TRUE), not null
#  runners_token          :string
#  build_coverage_regex   :string
#  build_allow_git_fetch  :boolean          default(TRUE), not null
#  build_timeout          :integer          default(3600), not null
39 40
#

gitlabhq committed
41 42
require 'spec_helper'

Douwe Maan committed
43
describe Project, models: true do
44
  describe 'associations' do
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    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 committed
64
    it { is_expected.to have_many(:commit_statuses) }
65 66 67 68 69 70
    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 committed
71 72
  end

73 74 75 76 77 78 79 80
  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) }
81 82
  end

83
  describe 'validation' do
84 85
    let!(:project) { create(:project) }

86 87
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
88
    it { is_expected.to validate_length_of(:name).is_within(0..255) }
89

90 91
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
92 93
    it { is_expected.to validate_length_of(:path).is_within(0..255) }
    it { is_expected.to validate_length_of(:description).is_within(0..2000) }
94
    it { is_expected.to validate_presence_of(:creator) }
95
    it { is_expected.to validate_length_of(:issues_tracker_id).is_within(0..255) }
96
    it { is_expected.to validate_presence_of(:namespace) }
97

98
    it 'should not allow new projects beyond user limits' do
99
      project2 = build(:project)
100 101 102
      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/)
103
    end
gitlabhq committed
104
  end
105 106 107
  
  describe 'project token' do
    it 'should set an random token if none provided' do
108 109
      project = FactoryGirl.create :empty_project, runners_token: ''
      expect(project.runners_token).not_to eq('')
110 111 112
    end

    it 'should not set an random toke if one provided' do
113 114
      project = FactoryGirl.create :empty_project, runners_token: 'my-token'
      expect(project.runners_token).to eq('my-token')
115 116
    end
  end
gitlabhq committed
117

118
  describe 'Respond to' do
119 120 121 122 123 124 125
    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 committed
126 127
  end

128 129 130 131 132 133 134 135
  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

136 137
  it 'should return valid url to repo' do
    project = Project.new(path: 'somewhere')
138
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
gitlabhq committed
139 140
  end

Douwe Maan committed
141 142 143 144 145 146
  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
147 148
  end

Douwe Maan committed
149 150 151 152 153 154
  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
155 156
  end

157
  describe 'last_activity methods' do
158
    let(:project) { create(:project) }
159
    let(:last_event) { double(created_at: Time.now) }
gitlabhq committed
160

161 162
    describe 'last_activity' do
      it 'should alias last_activity to last_event' do
163
        allow(project).to receive(:last_event).and_return(last_event)
164
        expect(project.last_activity).to eq(last_event)
165
      end
gitlabhq committed
166 167
    end

168 169
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
170
        create(:event, project: project)
171
        expect(project.last_activity_at.to_i).to eq(last_event.created_at.to_i)
172
      end
173

174
      it 'returns the project\'s last update date if it has no events' do
175
        expect(project.last_activity_date).to eq(project.updated_at)
176
      end
177 178
    end
  end
179

180 181
  describe '#get_issue' do
    let(:project) { create(:empty_project) }
182
    let!(:issue)  { create(:issue, project: project) }
183 184 185 186 187 188

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

189 190 191 192
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

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 219 220 221 222 223 224 225
      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

226
  describe :update_merge_requests do
227
    let(:project) { create(:project) }
228 229 230 231
    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 }
232

233
    it 'should close merge request if last commit from source branch was pushed to target branch' do
234 235
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.target_branch}", key.user)
      merge_request.reload
236
      expect(merge_request.merged?).to be_truthy
237 238
    end

239
    it 'should update merge request commits with new one if pushed to source branch' do
240 241
      project.update_merge_requests(prev_commit_id, commit_id, "refs/heads/#{merge_request.source_branch}", key.user)
      merge_request.reload
242
      expect(merge_request.last_commit.id).to eq(commit_id)
243 244
    end
  end
245 246 247 248 249

  describe :find_with_namespace do
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
Dmitriy Zaporozhets committed
250
        @project = create(:project, name: 'gitlabhq', namespace: @group)
251 252
      end

253
      it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) }
254
      it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) }
255
      it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil }
256 257 258 259 260 261 262
    end
  end

  describe :to_param do
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
263
        @project = create(:project, name: 'gitlabhq', namespace: @group)
264 265
      end

Vinnie Okada committed
266
      it { expect(@project.to_param).to eq('gitlabhq') }
267 268
    end
  end
Dmitriy Zaporozhets committed
269

270
  describe :repository do
Dmitriy Zaporozhets committed
271 272
    let(:project) { create(:project) }

273
    it 'should return valid repo' do
274
      expect(project.repository).to be_kind_of(Repository)
Dmitriy Zaporozhets committed
275 276
    end
  end
277

278
  describe :default_issues_tracker? do
279 280 281 282
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

    it "should be true if used internal tracker" do
283
      expect(project.default_issues_tracker?).to be_truthy
284 285 286
    end

    it "should be false if used other tracker" do
287
      expect(ext_project.default_issues_tracker?).to be_falsey
288 289 290
    end
  end

Andrew8xx8 committed
291 292 293 294
  describe :can_have_issues_tracker_id? do
    let(:project) { create(:project) }
    let(:ext_project) { create(:redmine_project) }

295
    it 'should be true for projects with external issues tracker if issues enabled' do
296
      expect(ext_project.can_have_issues_tracker_id?).to be_truthy
297
    end
Andrew8xx8 committed
298

299
    it 'should be false for projects with internal issue tracker if issues enabled' do
300
      expect(project.can_have_issues_tracker_id?).to be_falsey
Andrew8xx8 committed
301 302
    end

303
    it 'should be always false if issues disabled' do
Andrew8xx8 committed
304 305 306
      project.issues_enabled = false
      ext_project.issues_enabled = false

307 308
      expect(project.can_have_issues_tracker_id?).to be_falsey
      expect(ext_project.can_have_issues_tracker_id?).to be_falsey
Andrew8xx8 committed
309 310
    end
  end
311 312

  describe :open_branches do
313
    let(:project) { create(:project) }
314 315 316 317 318

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

319 320
    it { expect(project.open_branches.map(&:name)).to include('feature') }
    it { expect(project.open_branches.map(&:name)).not_to include('master') }
321
  end
Ciro Santilli committed
322

323 324
  describe '#star_count' do
    it 'counts stars from multiple users' do
Ciro Santilli committed
325 326 327 328 329
      user1 = create :user
      user2 = create :user
      project = create :project, :public

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

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

Ciro Santilli committed
334
      user2.toggle_star(project)
335 336 337
      project.reload
      expect(project.reload.star_count).to eq(2)

Ciro Santilli committed
338
      user1.toggle_star(project)
339 340 341
      project.reload
      expect(project.reload.star_count).to eq(1)

Ciro Santilli committed
342
      user2.toggle_star(project)
343 344 345 346
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

347
    it 'counts stars on the right project' do
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
      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 committed
378 379
    end
  end
380 381 382 383 384 385

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

    it 'should be true if avatar is image' do
      project.update_attribute(:avatar, 'uploads/avatar.png')
386
      expect(project.avatar_type).to be_truthy
387 388 389 390
    end

    it 'should be false if avatar is html page' do
      project.update_attribute(:avatar, 'uploads/avatar.html')
391
      expect(project.avatar_type).to eq(['only images allowed'])
392 393
    end
  end
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424

  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
425 426 427

  describe :ci_commit do
    let(:project) { create :project }
428
    let(:commit) { create :ci_commit, project: project }
429 430 431

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

433
  describe :builds_enabled do
434 435
    let(:project) { create :project }

436
    before { project.builds_enabled = true }
437

438 439 440
    subject { project.builds_enabled }

    it { expect(project.builds_enabled?).to be_truthy }
441
  end
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470

  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
471 472 473
          # 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)
474 475 476 477 478 479 480 481
        end
      end

      it 'sorts Projects by the amount of notes in descending order' do
        expect(subject).to eq([project2, project1])
      end
    end
  end
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

  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
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 526 527 528 529 530 531 532

  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
533
        project.runners << specific_runner
534 535 536 537 538 539 540 541 542
        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
543
        project.runners << specific_runner
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
        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
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585

  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 committed
586
end