BigW Consortium Gitlab

project_spec.rb 91.5 KB
Newer Older
gitlabhq committed
1 2
require 'spec_helper'

3
describe Project do
4
  describe 'associations' do
5 6 7 8
    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) }
ubudzisz committed
9
    it { is_expected.to have_many(:services) }
10 11 12 13 14
    it { is_expected.to have_many(:events) }
    it { is_expected.to have_many(:merge_requests) }
    it { is_expected.to have_many(:issues) }
    it { is_expected.to have_many(:milestones) }
    it { is_expected.to have_many(:project_members).dependent(:delete_all) }
15
    it { is_expected.to have_many(:users).through(:project_members) }
16 17 18 19
    it { is_expected.to have_many(:requesters).dependent(:delete_all) }
    it { is_expected.to have_many(:notes) }
    it { is_expected.to have_many(:snippets).class_name('ProjectSnippet') }
    it { is_expected.to have_many(:deploy_keys_projects) }
20
    it { is_expected.to have_many(:deploy_keys) }
21 22 23 24 25 26
    it { is_expected.to have_many(:hooks) }
    it { is_expected.to have_many(:protected_branches) }
    it { is_expected.to have_one(:forked_project_link) }
    it { is_expected.to have_one(:slack_service) }
    it { is_expected.to have_one(:microsoft_teams_service) }
    it { is_expected.to have_one(:mattermost_service) }
27
    it { is_expected.to have_one(:packagist_service) }
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
    it { is_expected.to have_one(:pushover_service) }
    it { is_expected.to have_one(:asana_service) }
    it { is_expected.to have_many(:boards) }
    it { is_expected.to have_one(:campfire_service) }
    it { is_expected.to have_one(:drone_ci_service) }
    it { is_expected.to have_one(:emails_on_push_service) }
    it { is_expected.to have_one(:pipelines_email_service) }
    it { is_expected.to have_one(:irker_service) }
    it { is_expected.to have_one(:pivotaltracker_service) }
    it { is_expected.to have_one(:hipchat_service) }
    it { is_expected.to have_one(:flowdock_service) }
    it { is_expected.to have_one(:assembla_service) }
    it { is_expected.to have_one(:slack_slash_commands_service) }
    it { is_expected.to have_one(:mattermost_slash_commands_service) }
    it { is_expected.to have_one(:gemnasium_service) }
    it { is_expected.to have_one(:buildkite_service) }
    it { is_expected.to have_one(:bamboo_service) }
    it { is_expected.to have_one(:teamcity_service) }
    it { is_expected.to have_one(:jira_service) }
    it { is_expected.to have_one(:redmine_service) }
    it { is_expected.to have_one(:custom_issue_tracker_service) }
    it { is_expected.to have_one(:bugzilla_service) }
    it { is_expected.to have_one(:gitlab_issue_tracker_service) }
    it { is_expected.to have_one(:external_wiki_service) }
    it { is_expected.to have_one(:project_feature) }
    it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
    it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
ubudzisz committed
55 56
    it { is_expected.to have_one(:last_event).class_name('Event') }
    it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
57
    it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
Kamil Trzcinski committed
58
    it { is_expected.to have_many(:commit_statuses) }
59
    it { is_expected.to have_many(:pipelines) }
60
    it { is_expected.to have_many(:builds) }
61
    it { is_expected.to have_many(:build_trace_section_names)}
62 63
    it { is_expected.to have_many(:runner_projects) }
    it { is_expected.to have_many(:runners) }
Kamil Trzcinski committed
64
    it { is_expected.to have_many(:active_runners) }
65 66
    it { is_expected.to have_many(:variables) }
    it { is_expected.to have_many(:triggers) }
67
    it { is_expected.to have_many(:pages_domains) }
68 69 70 71 72 73 74 75 76
    it { is_expected.to have_many(:labels).class_name('ProjectLabel') }
    it { is_expected.to have_many(:users_star_projects) }
    it { is_expected.to have_many(:environments) }
    it { is_expected.to have_many(:deployments) }
    it { is_expected.to have_many(:todos) }
    it { is_expected.to have_many(:releases) }
    it { is_expected.to have_many(:lfs_objects_projects) }
    it { is_expected.to have_many(:project_group_links) }
    it { is_expected.to have_many(:notification_settings).dependent(:delete_all) }
ubudzisz committed
77
    it { is_expected.to have_many(:forks).through(:forked_project_links) }
78
    it { is_expected.to have_many(:uploads).dependent(:destroy) }
79
    it { is_expected.to have_many(:pipeline_schedules) }
80
    it { is_expected.to have_many(:members_and_requesters) }
81
    it { is_expected.to have_one(:cluster) }
82

83 84
    context 'after initialized' do
      it "has a project_feature" do
85
        expect(described_class.new.project_feature).to be_present
86 87 88
      end
    end

89
    describe '#members & #requesters' do
90
      let(:project) { create(:project, :public, :access_requestable) }
91 92 93 94 95 96 97
      let(:requester) { create(:user) }
      let(:developer) { create(:user) }
      before do
        project.request_access(requester)
        project.team << [developer, :developer]
      end

98 99
      it_behaves_like 'members and requesters associations' do
        let(:namespace) { project }
100 101
      end
    end
102 103 104 105 106

    describe '#boards' do
      it 'raises an error when attempting to add more than one board to the project' do
        subject.boards.build

107
        expect { subject.boards.build }.to raise_error(Project::BoardLimitExceeded, 'Number of permitted boards exceeded')
108 109 110
        expect(subject.boards.size).to eq 1
      end
    end
gitlabhq committed
111 112
  end

113 114 115 116 117 118
  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) }
119
    it { is_expected.to include_module(Gitlab::CurrentSettings) }
120 121
    it { is_expected.to include_module(Referable) }
    it { is_expected.to include_module(Sortable) }
122 123
  end

124
  describe 'validation' do
125
    let!(:project) { create(:project) }
126

127 128
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
129
    it { is_expected.to validate_length_of(:name).is_at_most(255) }
130

131 132
    it { is_expected.to validate_presence_of(:path) }
    it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) }
133 134 135 136
    it { is_expected.to validate_length_of(:path).is_at_most(255) }

    it { is_expected.to validate_length_of(:description).is_at_most(2000) }

137 138 139
    it { is_expected.to validate_length_of(:ci_config_path).is_at_most(255) }
    it { is_expected.to allow_value('').for(:ci_config_path) }
    it { is_expected.not_to allow_value('test/../foo').for(:ci_config_path) }
140

141
    it { is_expected.to validate_presence_of(:creator) }
142

143
    it { is_expected.to validate_presence_of(:namespace) }
144

145
    it { is_expected.to validate_presence_of(:repository_storage) }
146

147
    it 'does not allow new projects beyond user limits' do
148
      project2 = build(:project)
149 150
      allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
      expect(project2).not_to be_valid
151
      expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/)
152
    end
153 154 155

    describe 'wiki path conflict' do
      context "when the new path has been used by the wiki of other Project" do
156
        it 'has an error on the name attribute' do
157
          new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
158 159 160 161 162 163 164

          expect(new_project).not_to be_valid
          expect(new_project.errors[:name].first).to eq('has already been taken')
        end
      end

      context "when the new wiki path has been used by the path of other Project" do
165
        it 'has an error on the name attribute' do
166 167
          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
168 169 170 171 172 173

          expect(new_project).not_to be_valid
          expect(new_project.errors[:name].first).to eq('has already been taken')
        end
      end
    end
174

175
    context 'repository storages inclusion' do
176
      let(:project2) { build(:project, repository_storage: 'missing') }
177 178

      before do
179
        storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } }
180 181 182
        allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
      end

183
      it "does not allow repository storages that don't match a label in the configuration" do
184 185 186 187
        expect(project2).not_to be_valid
        expect(project2.errors[:repository_storage].first).to match(/is not included in the list/)
      end
    end
188

189
    it 'does not allow an invalid URI as import_url' do
190
      project2 = build(:project, import_url: 'invalid://')
191 192 193 194

      expect(project2).not_to be_valid
    end

195
    it 'does allow a valid URI as import_url' do
196
      project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
197

198 199
      expect(project2).to be_valid
    end
200

201
    it 'allows an empty URI' do
202
      project2 = build(:project, import_url: '')
203

204
      expect(project2).to be_valid
205 206 207
    end

    it 'does not produce import data on an empty URI' do
208
      project2 = build(:project, import_url: '')
209 210 211 212 213

      expect(project2.import_data).to be_nil
    end

    it 'does not produce import data on an invalid URI' do
214
      project2 = build(:project, import_url: 'test://')
215 216 217

      expect(project2.import_data).to be_nil
    end
218

219
    it "does not allow blocked import_url localhost" do
220
      project2 = build(:project, import_url: 'http://localhost:9000/t.git')
221 222 223 224 225 226

      expect(project2).to be_invalid
      expect(project2.errors[:import_url]).to include('imports are not allowed from that URL')
    end

    it "does not allow blocked import_url port" do
227
      project2 = build(:project, import_url: 'http://github.com:25/t.git')
228 229 230 231 232

      expect(project2).to be_invalid
      expect(project2.errors[:import_url]).to include('imports are not allowed from that URL')
    end

233 234
    describe 'project pending deletion' do
      let!(:project_pending_deletion) do
235
        create(:project,
236 237 238
               pending_delete: true)
      end
      let(:new_project) do
239
        build(:project,
240 241 242 243 244 245 246 247 248 249 250 251
              name: project_pending_deletion.name,
              namespace: project_pending_deletion.namespace)
      end

      before do
        new_project.validate
      end

      it 'contains errors related to the project being deleted' do
        expect(new_project.errors.full_messages.first).to eq('The project is still being deleted. Please try again later.')
      end
    end
252 253 254

    describe 'path validation' do
      it 'allows paths reserved on the root namespace' do
255
        project = build(:project, path: 'api')
256 257 258 259 260

        expect(project).to be_valid
      end

      it 'rejects paths reserved on another level' do
261
        project = build(:project, path: 'tree')
262 263 264

        expect(project).not_to be_valid
      end
265 266 267

      it 'rejects nested paths' do
        parent = create(:group, :nested, path: 'environments')
268
        project = build(:project, path: 'folders', namespace: parent)
269 270 271

        expect(project).not_to be_valid
      end
272 273 274

      it 'allows a reserved group name' do
        parent = create(:group)
275
        project = build(:project, path: 'avatar', namespace: parent)
276 277 278

        expect(project).to be_valid
      end
279 280 281 282 283 284

      it 'allows a path ending in a period' do
        project = build(:project, path: 'foo.')

        expect(project).to be_valid
      end
285
    end
gitlabhq committed
286
  end
287

288
  describe 'project token' do
289
    it 'sets an random token if none provided' do
290
      project = FactoryGirl.create :project, runners_token: ''
291
      expect(project.runners_token).not_to eq('')
292 293
    end

ubudzisz committed
294
    it 'does not set an random token if one provided' do
295
      project = FactoryGirl.create :project, runners_token: 'my-token'
296
      expect(project.runners_token).to eq('my-token')
297 298
    end
  end
gitlabhq committed
299

300
  describe 'Respond to' do
301 302 303 304 305
    it { is_expected.to respond_to(:url_to_repo) }
    it { is_expected.to respond_to(:repo_exists?) }
    it { is_expected.to respond_to(:execute_hooks) }
    it { is_expected.to respond_to(:owner) }
    it { is_expected.to respond_to(:path_with_namespace) }
306
    it { is_expected.to respond_to(:full_path) }
gitlabhq committed
307 308
  end

309
  describe 'delegation' do
310 311 312 313 314 315 316 317
    [:add_guest, :add_reporter, :add_developer, :add_master, :add_user, :add_users].each do |method|
      it { is_expected.to delegate_method(method).to(:team) }
    end

    it { is_expected.to delegate_method(:empty_repo?).to(:repository) }
    it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) }
    it { is_expected.to delegate_method(:count).to(:forks).with_prefix(true) }
    it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
318 319
  end

320
  describe '#to_reference' do
321
    let(:owner)     { create(:user, name: 'Gitlab') }
322
    let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
323
    let(:project)   { create(:project, path: 'sample-project', namespace: namespace) }
324
    let(:group)     { create(:group, name: 'Group', path: 'sample-group', owner: owner) }
325

326
    context 'when nil argument' do
327 328 329 330 331
      it 'returns nil' do
        expect(project.to_reference).to be_nil
      end
    end

332
    context 'when full is true' do
333
      it 'returns complete path to the project' do
334 335 336
        expect(project.to_reference(full: true)).to          eq 'sample-namespace/sample-project'
        expect(project.to_reference(project, full: true)).to eq 'sample-namespace/sample-project'
        expect(project.to_reference(group, full: true)).to   eq 'sample-namespace/sample-project'
337 338 339 340 341 342 343 344 345 346
      end
    end

    context 'when same project argument' do
      it 'returns nil' do
        expect(project.to_reference(project)).to be_nil
      end
    end

    context 'when cross namespace project argument' do
347
      let(:another_namespace_project) { create(:project, name: 'another-project') }
348 349 350 351 352 353 354

      it 'returns complete path to the project' do
        expect(project.to_reference(another_namespace_project)).to eq 'sample-namespace/sample-project'
      end
    end

    context 'when same namespace / cross-project argument' do
355
      let(:another_project) { create(:project, namespace: namespace) }
356

357
      it 'returns path to the project' do
358 359 360
        expect(project.to_reference(another_project)).to eq 'sample-project'
      end
    end
361

362 363
    context 'when different namespace / cross-project argument' do
      let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) }
364
      let(:another_project)   { create(:project, path: 'another-project', namespace: another_namespace) }
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381

      it 'returns full path to the project' do
        expect(project.to_reference(another_project)).to eq 'sample-namespace/sample-project'
      end
    end

    context 'when argument is a namespace' do
      context 'with same project path' do
        it 'returns path to the project' do
          expect(project.to_reference(namespace)).to eq 'sample-project'
        end
      end

      context 'with different project path' do
        it 'returns full path to the project' do
          expect(project.to_reference(group)).to eq 'sample-namespace/sample-project'
        end
382 383
      end
    end
384 385 386 387 388
  end

  describe '#to_human_reference' do
    let(:owner) { create(:user, name: 'Gitlab') }
    let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
389
    let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
390 391 392 393 394 395 396 397 398 399 400 401 402 403

    context 'when nil argument' do
      it 'returns nil' do
        expect(project.to_human_reference).to be_nil
      end
    end

    context 'when same project argument' do
      it 'returns nil' do
        expect(project.to_human_reference(project)).to be_nil
      end
    end

    context 'when cross namespace project argument' do
404
      let(:another_namespace_project) { create(:project, name: 'another-project') }
405 406 407 408 409 410 411

      it 'returns complete name with namespace of the project' do
        expect(project.to_human_reference(another_namespace_project)).to eq 'Gitlab / Sample project'
      end
    end

    context 'when same namespace / cross-project argument' do
412
      let(:another_project) { create(:project, namespace: namespace) }
413 414 415 416

      it 'returns name of the project' do
        expect(project.to_human_reference(another_project)).to eq 'Sample project'
      end
417 418 419
    end
  end

420 421 422 423 424 425 426 427 428 429 430 431
  describe '#merge_method' do
    it 'returns "ff" merge_method when ff is enabled' do
      project = build(:project, merge_requests_ff_only_enabled: true)
      expect(project.merge_method).to be :ff
    end

    it 'returns "merge" merge_method when ff is disabled' do
      project = build(:project, merge_requests_ff_only_enabled: false)
      expect(project.merge_method).to be :merge
    end
  end

432
  describe '#repository_storage_path' do
433
    let(:project) { create(:project) }
434 435

    it 'returns the repository storage path' do
436
      expect(Dir.exist?(project.repository_storage_path)).to be(true)
437 438 439
    end
  end

440
  it 'returns valid url to repo' do
441
    project = described_class.new(path: 'somewhere')
442
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
gitlabhq committed
443 444
  end

Douwe Maan committed
445
  describe "#web_url" do
446
    let(:project) { create(:project, path: "somewhere") }
Douwe Maan committed
447 448

    it 'returns the full web URL for this repo' do
449
      expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
Douwe Maan committed
450
    end
451 452
  end

453
  describe "#new_issue_address" do
454
    let(:project) { create(:project, path: "somewhere") }
455 456
    let(:user) { create(:user) }

457 458 459 460 461 462
    context 'incoming email enabled' do
      before do
        stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
      end

      it 'returns the address to create a new issue' do
463
        address = "p+#{project.full_path}+#{user.incoming_email_token}@gl.ab"
464 465 466 467 468 469 470 471 472

        expect(project.new_issue_address(user)).to eq(address)
      end
    end

    context 'incoming email disabled' do
      before do
        stub_incoming_email_setting(enabled: false)
      end
473

474 475 476
      it 'returns nil' do
        expect(project.new_issue_address(user)).to be_nil
      end
477 478 479
    end
  end

480
  describe 'last_activity methods' do
481 482
    let(:timestamp) { 2.hours.ago }
    # last_activity_at gets set to created_at upon creation
483
    let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
gitlabhq committed
484

485
    describe 'last_activity' do
486
      it 'alias last_activity to last_event' do
487
        last_event = create(:event, :closed, project: project)
488

489
        expect(project.last_activity).to eq(last_event)
490
      end
gitlabhq committed
491 492
    end

493 494
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
495
        new_event = create(:event, :closed, project: project, created_at: Time.now)
496

497
        project.reload
498
        expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
499
      end
500

501
      it 'returns the project\'s last update date if it has no events' do
502
        expect(project.last_activity_date).to eq(project.updated_at)
503
      end
504 505
    end
  end
506

507
  describe '#get_issue' do
508
    let(:project) { create(:project) }
509
    let!(:issue)  { create(:issue, project: project) }
510 511 512 513 514
    let(:user)    { create(:user) }

    before do
      project.team << [user, :developer]
    end
515 516 517

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

521 522 523 524
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

525
      it 'returns nil when no issue found' do
526 527 528 529 530 531
        expect(project.get_issue(999, user)).to be_nil
      end

      it "returns nil when user doesn't have access" do
        user = create(:user)
        expect(project.get_issue(issue.iid, user)).to eq nil
532 533 534 535
      end
    end

    context 'with external issues tracker' do
536
      let!(:internal_issue) { create(:issue, project: project) }
537
      before do
538
        allow(project).to receive(:external_issue_tracker).and_return(true)
539 540
      end

541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
      context 'when internal issues are enabled' do
        it 'returns interlan issue' do
          issue = project.get_issue(internal_issue.iid, user)

          expect(issue).to be_kind_of(Issue)
          expect(issue.iid).to eq(internal_issue.iid)
          expect(issue.project).to eq(project)
        end

        it 'returns an ExternalIssue when internal issue does not exists' do
          issue = project.get_issue('FOO-1234', user)

          expect(issue).to be_kind_of(ExternalIssue)
          expect(issue.iid).to eq('FOO-1234')
          expect(issue.project).to eq(project)
        end
      end

      context 'when internal issues are disabled' do
        before do
          project.issues_enabled = false
          project.save!
        end

        it 'returns always an External issues' do
          issue = project.get_issue(internal_issue.iid, user)
          expect(issue).to be_kind_of(ExternalIssue)
          expect(issue.iid).to eq(internal_issue.iid.to_s)
          expect(issue.project).to eq(project)
        end

        it 'returns an ExternalIssue when internal issue does not exists' do
          issue = project.get_issue('FOO-1234', user)
          expect(issue).to be_kind_of(ExternalIssue)
          expect(issue.iid).to eq('FOO-1234')
          expect(issue.project).to eq(project)
        end
578 579 580 581 582
      end
    end
  end

  describe '#issue_exists?' do
583
    let(:project) { create(:project) }
584 585 586 587 588 589 590 591 592 593 594 595

    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

596
  describe '#to_param' do
597 598 599
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
600
        @project = create(:project, name: 'gitlabhq', namespace: @group)
601 602
      end

Vinnie Okada committed
603
      it { expect(@project.to_param).to eq('gitlabhq') }
604
    end
605 606 607

    context 'with invalid path' do
      it 'returns previous path to keep project suitable for use in URLs when persisted' do
608
        project = create(:project, path: 'gitlab')
609 610 611 612 613 614 615
        project.path = 'foo&bar'

        expect(project).not_to be_valid
        expect(project.to_param).to eq 'gitlab'
      end

      it 'returns current path when new record' do
616
        project = build(:project, path: 'gitlab')
617 618 619 620 621 622
        project.path = 'foo&bar'

        expect(project).not_to be_valid
        expect(project.to_param).to eq 'foo&bar'
      end
    end
623
  end
Dmitriy Zaporozhets committed
624

625
  describe '#repository' do
626
    let(:project) { create(:project, :repository) }
Dmitriy Zaporozhets committed
627

628
    it 'returns valid repo' do
629
      expect(project.repository).to be_kind_of(Repository)
Dmitriy Zaporozhets committed
630 631
    end
  end
632

633
  describe '#default_issues_tracker?' do
634
    it "is true if used internal tracker" do
635
      project = build(:project)
636

637
      expect(project.default_issues_tracker?).to be_truthy
638 639
    end

640
    it "is false if used other tracker" do
641 642 643 644
      # NOTE: The current nature of this factory requires persistence
      project = create(:redmine_project)

      expect(project.default_issues_tracker?).to be_falsey
645 646 647
    end
  end

648
  describe '#external_issue_tracker' do
649
    let(:project) { create(:project) }
650 651 652
    let(:ext_project) { create(:redmine_project) }

    context 'on existing projects with no value for has_external_issue_tracker' do
653
      before do
654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
        project.update_column(:has_external_issue_tracker, nil)
        ext_project.update_column(:has_external_issue_tracker, nil)
      end

      it 'updates the has_external_issue_tracker boolean' do
        expect do
          project.external_issue_tracker
        end.to change { project.reload.has_external_issue_tracker }.to(false)

        expect do
          ext_project.external_issue_tracker
        end.to change { ext_project.reload.has_external_issue_tracker }.to(true)
      end
    end

    it 'returns nil and does not query services when there is no external issue tracker' do
      expect(project).not_to receive(:services)

      expect(project.external_issue_tracker).to eq(nil)
    end

    it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do
      ext_project.reload # Factory returns a project with changed attributes
      expect(ext_project).to receive(:services).once.and_call_original

      2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) }
    end
  end

683
  describe '#cache_has_external_issue_tracker' do
684
    let(:project) { create(:project, has_external_issue_tracker: nil) }
685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702

    it 'stores true if there is any external_issue_tracker' do
      services = double(:service, external_issue_trackers: [RedmineService.new])
      expect(project).to receive(:services).and_return(services)

      expect do
        project.cache_has_external_issue_tracker
      end.to change { project.has_external_issue_tracker}.to(true)
    end

    it 'stores false if there is no external_issue_tracker' do
      services = double(:service, external_issue_trackers: [])
      expect(project).to receive(:services).and_return(services)

      expect do
        project.cache_has_external_issue_tracker
      end.to change { project.has_external_issue_tracker}.to(false)
    end
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740

    it 'does not cache data when in a read-only GitLab instance' do
      allow(Gitlab::Database).to receive(:read_only?) { true }

      expect do
        project.cache_has_external_issue_tracker
      end.not_to change { project.has_external_issue_tracker }
    end
  end

  describe '#cache_has_external_wiki' do
    let(:project) { create(:project, has_external_wiki: nil) }

    it 'stores true if there is any external_wikis' do
      services = double(:service, external_wikis: [ExternalWikiService.new])
      expect(project).to receive(:services).and_return(services)

      expect do
        project.cache_has_external_wiki
      end.to change { project.has_external_wiki}.to(true)
    end

    it 'stores false if there is no external_wikis' do
      services = double(:service, external_wikis: [])
      expect(project).to receive(:services).and_return(services)

      expect do
        project.cache_has_external_wiki
      end.to change { project.has_external_wiki}.to(false)
    end

    it 'does not cache data when in a read-only GitLab instance' do
      allow(Gitlab::Database).to receive(:read_only?) { true }

      expect do
        project.cache_has_external_wiki
      end.not_to change { project.has_external_wiki }
    end
741 742
  end

743
  describe '#has_wiki?' do
744 745 746
    let(:no_wiki_project)       { create(:project, :wiki_disabled, has_external_wiki: false) }
    let(:wiki_enabled_project)  { create(:project) }
    let(:external_wiki_project) { create(:project, has_external_wiki: true) }
747 748 749 750 751 752 753 754

    it 'returns true if project is wiki enabled or has external wiki' do
      expect(wiki_enabled_project).to have_wiki
      expect(external_wiki_project).to have_wiki
      expect(no_wiki_project).not_to have_wiki
    end
  end

755
  describe '#external_wiki' do
756
    let(:project) { create(:project) }
757

758 759 760 761 762
    context 'with an active external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: true)
        project.external_wiki
      end
763

764 765 766
      it 'sets :has_external_wiki as true' do
        expect(project.has_external_wiki).to be(true)
      end
767

768 769
      it 'sets :has_external_wiki as false if an external wiki service is destroyed later' do
        expect(project.has_external_wiki).to be(true)
770

771 772 773 774
        project.services.external_wikis.first.destroy

        expect(project.has_external_wiki).to be(false)
      end
775 776
    end

777 778 779 780
    context 'with an inactive external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: false)
      end
781

782 783 784
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end
785 786
    end

787 788 789 790
    context 'with no external wiki' do
      before do
        project.external_wiki
      end
791

792 793 794 795 796 797 798 799 800 801 802
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end

      it 'sets :has_external_wiki as true if an external wiki service is created later' do
        expect(project.has_external_wiki).to be(false)

        create(:service, project: project, type: 'ExternalWikiService', active: true)

        expect(project.has_external_wiki).to be(true)
      end
803 804 805
    end
  end

806 807
  describe '#star_count' do
    it 'counts stars from multiple users' do
Ciro Santilli committed
808 809
      user1 = create :user
      user2 = create :user
810
      project = create(:project, :public)
Ciro Santilli committed
811 812

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

Ciro Santilli committed
814
      user1.toggle_star(project)
815 816
      expect(project.reload.star_count).to eq(1)

Ciro Santilli committed
817
      user2.toggle_star(project)
818 819 820
      project.reload
      expect(project.reload.star_count).to eq(2)

Ciro Santilli committed
821
      user1.toggle_star(project)
822 823 824
      project.reload
      expect(project.reload.star_count).to eq(1)

Ciro Santilli committed
825
      user2.toggle_star(project)
826 827 828 829
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

830
    it 'counts stars on the right project' do
831
      user = create :user
832 833
      project1 = create(:project, :public)
      project2 = create(:project, :public)
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

      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
861 862
    end
  end
863

864
  describe '#avatar_type' do
865
    let(:project) { create(:project) }
866

867
    it 'is true if avatar is image' do
868
      project.update_attribute(:avatar, 'uploads/avatar.png')
869
      expect(project.avatar_type).to be_truthy
870 871
    end

872
    it 'is false if avatar is html page' do
873
      project.update_attribute(:avatar, 'uploads/avatar.html')
874
      expect(project.avatar_type).to eq(['only images allowed'])
875 876
    end
  end
877

878
  describe '#avatar_url' do
879 880
    subject { project.avatar_url }

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

883
    context 'when avatar file is uploaded' do
884
      let(:project) { create(:project, :public, :with_avatar) }
885
      let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" }
886
      let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
887

888 889 890 891 892 893 894 895
      it 'shows correct url' do
        expect(project.avatar_url).to eq(avatar_path)
        expect(project.avatar_url(only_path: false)).to eq([gitlab_host, avatar_path].join)

        allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)

        expect(project.avatar_url).to eq([gitlab_host, avatar_path].join)
      end
896 897 898 899 900 901 902
    end

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

903
      let(:avatar_path) { "/#{project.full_path}/avatar" }
904

905
      it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
906
    end
907 908

    context 'when git repo is empty' do
909
      let(:project) { create(:project) }
910

911
      it { is_expected.to eq nil }
912
    end
913
  end
914

915
  describe '#pipeline_for' do
916
    let(:project) { create(:project, :repository) }
917
    let!(:pipeline) { create_pipeline }
918

919 920
    shared_examples 'giving the correct pipeline' do
      it { is_expected.to eq(pipeline) }
921

922 923
      context 'return latest' do
        let!(:pipeline2) { create_pipeline }
924

925
        it { is_expected.to eq(pipeline2) }
926
      end
927 928 929 930 931 932 933 934 935 936 937 938 939
    end

    context 'with explicit sha' do
      subject { project.pipeline_for('master', pipeline.sha) }

      it_behaves_like 'giving the correct pipeline'
    end

    context 'with implicit sha' do
      subject { project.pipeline_for('master') }

      it_behaves_like 'giving the correct pipeline'
    end
940

941 942 943 944 945
    def create_pipeline
      create(:ci_pipeline,
             project: project,
             ref: 'master',
             sha: project.commit('master').sha)
946
    end
947
  end
948

949
  describe '#builds_enabled' do
950
    let(:project) { create(:project) }
951

952 953 954
    subject { project.builds_enabled }

    it { expect(project.builds_enabled?).to be_truthy }
955
  end
956

957
  describe '.with_shared_runners' do
958
    subject { described_class.with_shared_runners }
959 960

    context 'when shared runners are enabled for project' do
961
      let!(:project) { create(:project, shared_runners_enabled: true) }
962 963 964 965 966 967 968

      it "returns a project" do
        is_expected.to eq([project])
      end
    end

    context 'when shared runners are disabled for project' do
969
      let!(:project) { create(:project, shared_runners_enabled: false) }
970 971 972 973 974 975 976

      it "returns an empty array" do
        is_expected.to be_empty
      end
    end
  end

977
  describe '.cached_count', :use_clean_rails_memory_store_caching do
978
    let(:group)     { create(:group, :public) }
979 980
    let!(:project1) { create(:project, :public, group: group) }
    let!(:project2) { create(:project, :public, group: group) }
981 982

    it 'returns total project count' do
983
      expect(described_class).to receive(:count).once.and_call_original
984 985

      3.times do
986
        expect(described_class.cached_count).to eq(2)
987 988 989 990
      end
    end
  end

991
  describe '.trending' do
Felipe Artur committed
992
    let(:group)    { create(:group, :public) }
993 994
    let(:project1) { create(:project, :public, group: group) }
    let(:project2) { create(:project, :public, group: group) }
995 996 997 998 999 1000 1001 1002

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

      create(:note_on_commit, project: project2)

1003
      TrendingProject.refresh!
1004 1005
    end

1006
    subject { described_class.trending.to_a }
1007

1008 1009
    it 'sorts projects by the amount of notes in descending order' do
      expect(subject).to eq([project1, project2])
1010
    end
1011 1012 1013 1014 1015 1016 1017 1018

    it 'does not take system notes into account' do
      10.times do
        create(:note_on_commit, project: project2, system: true)
      end

      expect(described_class.trending.to_a).to eq([project1, project2])
    end
1019
  end
1020

1021 1022 1023 1024
  describe '.starred_by' do
    it 'returns only projects starred by the given user' do
      user1 = create(:user)
      user2 = create(:user)
1025 1026 1027
      project1 = create(:project)
      project2 = create(:project)
      create(:project)
1028 1029 1030
      user1.toggle_star(project1)
      user2.toggle_star(project2)

1031
      expect(described_class.starred_by(user1)).to contain_exactly(project1)
1032 1033 1034
    end
  end

1035
  describe '.visible_to_user' do
1036
    let!(:project) { create(:project, :private) }
1037 1038 1039 1040 1041 1042
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
1043
        project.add_user(user, Gitlab::Access::MASTER)
1044 1045 1046 1047 1048 1049 1050 1051 1052
      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
1053

1054
  context 'repository storage by default' do
1055
    let(:project) { create(:project) }
1056 1057

    before do
1058
      storages = {
1059
        'default' => { 'path' => 'tmp/tests/repositories' },
1060
        'picked'  => { 'path' => 'tmp/tests/repositories' }
1061
      }
1062 1063 1064
      allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
    end

1065 1066 1067 1068 1069
    it 'picks storage from ApplicationSetting' do
      expect_any_instance_of(ApplicationSetting).to receive(:pick_repository_storage).and_return('picked')

      expect(project.repository_storage).to eq('picked')
    end
1070 1071
  end

1072
  context 'shared runners by default' do
1073
    let(:project) { create(:project) }
1074 1075 1076 1077

    subject { project.shared_runners_enabled }

    context 'are enabled' do
1078 1079 1080
      before do
        stub_application_setting(shared_runners_enabled: true)
      end
1081 1082 1083 1084 1085

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
1086 1087 1088
      before do
        stub_application_setting(shared_runners_enabled: false)
      end
1089 1090 1091 1092 1093

      it { is_expected.to be_falsey }
    end
  end

1094
  describe '#any_runners' do
1095
    let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
1096 1097
    let(:specific_runner) { create(:ci_runner) }
    let(:shared_runner) { create(:ci_runner, :shared) }
1098 1099 1100

    context 'for shared runners disabled' do
      let(:shared_runners_enabled) { false }
1101

1102
      it 'has no runners available' do
1103 1104
        expect(project.any_runners?).to be_falsey
      end
1105

1106
      it 'has a specific runner' do
1107
        project.runners << specific_runner
1108 1109
        expect(project.any_runners?).to be_truthy
      end
1110

1111
      it 'has a shared runner, but they are prohibited to use' do
1112 1113 1114
        shared_runner
        expect(project.any_runners?).to be_falsey
      end
1115

1116
      it 'checks the presence of specific runner' do
1117
        project.runners << specific_runner
1118 1119 1120
        expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
      end
    end
1121

1122 1123
    context 'for shared runners enabled' do
      let(:shared_runners_enabled) { true }
1124

1125
      it 'has a shared runner' do
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
        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
1136

1137 1138 1139 1140 1141 1142
  describe '#shared_runners' do
    let!(:runner) { create(:ci_runner, :shared) }

    subject { project.shared_runners }

    context 'when shared runners are enabled for project' do
1143
      let!(:project) { create(:project, shared_runners_enabled: true) }
1144 1145 1146 1147 1148 1149 1150

      it "returns a list of shared runners" do
        is_expected.to eq([runner])
      end
    end

    context 'when shared runners are disabled for project' do
1151
      let!(:project) { create(:project, shared_runners_enabled: false) }
1152 1153 1154 1155 1156 1157 1158

      it "returns a empty list" do
        is_expected.to be_empty
      end
    end
  end

1159
  describe '#visibility_level_allowed?' do
1160
    let(:project) { create(:project, :internal) }
1161 1162 1163 1164 1165 1166 1167 1168

    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
1169 1170
      let(:project)        { create(:project, :internal) }
      let(:forked_project) { create(:project, forked_from_project: project) }
1171 1172 1173 1174 1175

      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
1176
  end
1177

1178
  describe '#pages_deployed?' do
1179
    let(:project) { create :project }
1180 1181 1182 1183

    subject { project.pages_deployed? }

    context 'if public folder does exist' do
1184 1185 1186
      before do
        allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
      end
1187 1188 1189 1190 1191 1192 1193 1194 1195

      it { is_expected.to be_truthy }
    end

    context "if public folder doesn't exist" do
      it { is_expected.to be_falsey }
    end
  end

1196 1197
  describe '#pages_url' do
    let(:group) { create :group, name: group_name }
1198
    let(:project) { create :project, namespace: group, name: project_name }
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222
    let(:domain) { 'Example.com' }

    subject { project.pages_url }

    before do
      allow(Settings.pages).to receive(:host).and_return(domain)
      allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
    end

    context 'group page' do
      let(:group_name) { 'Group' }
      let(:project_name) { 'group.example.com' }

      it { is_expected.to eq("http://group.example.com") }
    end

    context 'project page' do
      let(:group_name) { 'Group' }
      let(:project_name) { 'Project' }

      it { is_expected.to eq("http://group.example.com/project") }
    end
  end

1223
  describe '.search' do
1224
    let(:project) { create(:project, description: 'kitten mittens') }
1225

1226 1227 1228
    it 'returns projects with a matching name' do
      expect(described_class.search(project.name)).to eq([project])
    end
1229

1230 1231 1232
    it 'returns projects with a partially matching name' do
      expect(described_class.search(project.name[0..2])).to eq([project])
    end
1233

1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
    it 'returns projects with a matching name regardless of the casing' do
      expect(described_class.search(project.name.upcase)).to eq([project])
    end

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

    it 'returns projects with a partially matching description' do
      expect(described_class.search('kitten')).to eq([project])
    end

    it 'returns projects with a matching description regardless of the casing' do
      expect(described_class.search('KITTEN')).to eq([project])
    end

    it 'returns projects with a matching path' do
      expect(described_class.search(project.path)).to eq([project])
    end

    it 'returns projects with a partially matching path' do
      expect(described_class.search(project.path[0..2])).to eq([project])
    end

    it 'returns projects with a matching path regardless of the casing' do
      expect(described_class.search(project.path.upcase)).to eq([project])
    end

    it 'returns projects with a matching namespace name' do
      expect(described_class.search(project.namespace.name)).to eq([project])
    end

    it 'returns projects with a partially matching namespace name' do
      expect(described_class.search(project.namespace.name[0..2])).to eq([project])
    end

    it 'returns projects with a matching namespace name regardless of the casing' do
      expect(described_class.search(project.namespace.name.upcase)).to eq([project])
    end
1273 1274 1275 1276 1277

    it 'returns projects when eager loading namespaces' do
      relation = described_class.all.includes(:namespace)

      expect(relation.search(project.namespace.name)).to eq([project])
1278
    end
1279 1280

    describe 'with pending_delete project' do
1281
      let(:pending_delete_project) { create(:project, pending_delete: true) }
1282 1283 1284 1285 1286 1287 1288

      it 'shows pending deletion project' do
        search_result = described_class.search(pending_delete_project.name)

        expect(search_result).to eq([pending_delete_project])
      end
    end
1289
  end
1290 1291

  describe '#expire_caches_before_rename' do
1292
    let(:project) { create(:project, :repository) }
1293 1294 1295 1296
    let(:repo)    { double(:repo, exists?: true) }
    let(:wiki)    { double(:wiki, exists?: true) }

    it 'expires the caches of the repository and wiki' do
1297 1298 1299
      allow(Repository).to receive(:new)
        .with('foo', project)
        .and_return(repo)
1300

1301 1302 1303
      allow(Repository).to receive(:new)
        .with('foo.wiki', project)
        .and_return(wiki)
1304

1305 1306
      expect(repo).to receive(:before_delete)
      expect(wiki).to receive(:before_delete)
1307 1308 1309 1310

      project.expire_caches_before_rename('foo')
    end
  end
1311 1312

  describe '.search_by_title' do
1313
    let(:project) { create(:project, name: 'kittens') }
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326

    it 'returns projects with a matching name' do
      expect(described_class.search_by_title(project.name)).to eq([project])
    end

    it 'returns projects with a partially matching name' do
      expect(described_class.search_by_title('kitten')).to eq([project])
    end

    it 'returns projects with a matching name regardless of the casing' do
      expect(described_class.search_by_title('KITTENS')).to eq([project])
    end
  end
1327 1328 1329 1330 1331

  context 'when checking projects from groups' do
    let(:private_group)    { create(:group, visibility_level: 0)  }
    let(:internal_group)   { create(:group, visibility_level: 10) }

1332 1333
    let(:private_project)  { create :project, :private, group: private_group }
    let(:internal_project) { create :project, :internal, group: internal_group }
1334 1335 1336 1337 1338 1339 1340 1341 1342

    context 'when group is private project can not be internal' do
      it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey }
    end

    context 'when group is internal project can not be public' do
      it { expect(internal_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PUBLIC)).to be_falsey }
    end
  end
1343

1344
  describe '#create_repository' do
1345
    let(:project) { create(:project, :repository) }
1346 1347 1348 1349 1350 1351 1352 1353
    let(:shell) { Gitlab::Shell.new }

    before do
      allow(project).to receive(:gitlab_shell).and_return(shell)
    end

    context 'using a regular repository' do
      it 'creates the repository' do
1354
        expect(shell).to receive(:add_repository)
1355
          .with(project.repository_storage, project.disk_path)
1356
          .and_return(true)
1357 1358 1359 1360 1361 1362 1363

        expect(project.repository).to receive(:after_create)

        expect(project.create_repository).to eq(true)
      end

      it 'adds an error if the repository could not be created' do
1364
        expect(shell).to receive(:add_repository)
1365
          .with(project.repository_storage, project.disk_path)
1366
          .and_return(false)
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383

        expect(project.repository).not_to receive(:after_create)

        expect(project.create_repository).to eq(false)
        expect(project.errors).not_to be_empty
      end
    end

    context 'using a forked repository' do
      it 'does nothing' do
        expect(project).to receive(:forked?).and_return(true)
        expect(shell).not_to receive(:add_repository)

        project.create_repository
      end
    end
  end
1384

1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
  describe '#ensure_repository' do
    let(:project) { create(:project, :repository) }
    let(:shell) { Gitlab::Shell.new }

    before do
      allow(project).to receive(:gitlab_shell).and_return(shell)
    end

    it 'creates the repository if it not exist' do
      allow(project).to receive(:repository_exists?)
        .and_return(false)

      allow(shell).to receive(:add_repository)
1398
        .with(project.repository_storage_path, project.disk_path)
1399 1400
        .and_return(true)

1401
      expect(project).to receive(:create_repository).with(force: true)
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413

      project.ensure_repository
    end

    it 'does not create the repository if it exists' do
      allow(project).to receive(:repository_exists?)
        .and_return(true)

      expect(project).not_to receive(:create_repository)

      project.ensure_repository
    end
1414 1415 1416 1417 1418 1419 1420 1421

    it 'creates the repository if it is a fork' do
      expect(project).to receive(:forked?).and_return(true)

      allow(project).to receive(:repository_exists?)
        .and_return(false)

      expect(shell).to receive(:add_repository)
1422
        .with(project.repository_storage, project.disk_path)
1423 1424 1425 1426
        .and_return(true)

      project.ensure_repository
    end
1427 1428
  end

1429
  describe '#user_can_push_to_empty_repo?' do
1430
    let(:project) { create(:project) }
1431
    let(:user)    { create(:user) }
1432

1433 1434 1435
    it 'returns false when default_branch_protection is in full protection and user is developer' do
      project.team << [user, :developer]
      stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
1436

1437
      expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
1438 1439
    end

1440 1441 1442
    it 'returns false when default_branch_protection only lets devs merge and user is dev' do
      project.team << [user, :developer]
      stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
1443

1444
      expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
1445 1446
    end

1447 1448 1449
    it 'returns true when default_branch_protection lets devs push and user is developer' do
      project.team << [user, :developer]
      stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
1450

1451 1452
      expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
    end
1453

1454 1455 1456
    it 'returns true when default_branch_protection is unprotected and user is developer' do
      project.team << [user, :developer]
      stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
1457

1458
      expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
1459
    end
1460

1461 1462
    it 'returns true when user is master' do
      project.team << [user, :master]
1463

1464
      expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
1465 1466 1467
    end
  end

1468
  describe '#container_registry_url' do
1469
    let(:project) { create(:project) }
1470

1471
    subject { project.container_registry_url }
1472

1473 1474 1475
    before do
      stub_container_registry_config(**registry_settings)
    end
1476 1477 1478

    context 'for enabled registry' do
      let(:registry_settings) do
1479 1480
        { enabled: true,
          host_port: 'example.com' }
1481 1482
      end

1483
      it { is_expected.not_to be_nil }
1484 1485 1486 1487
    end

    context 'for disabled registry' do
      let(:registry_settings) do
1488
        { enabled: false }
1489 1490 1491 1492 1493 1494
      end

      it { is_expected.to be_nil }
    end
  end

1495
  describe '#has_container_registry_tags?' do
1496
    let(:project) { create(:project) }
1497 1498

    context 'when container registry is enabled' do
1499 1500 1501
      before do
        stub_container_registry_config(enabled: true)
      end
1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538

      context 'when tags are present for multi-level registries' do
        before do
          create(:container_repository, project: project, name: 'image')

          stub_container_registry_tags(repository: /image/,
                                       tags: %w[latest rc1])
        end

        it 'should have image tags' do
          expect(project).to have_container_registry_tags
        end
      end

      context 'when tags are present for root repository' do
        before do
          stub_container_registry_tags(repository: project.full_path,
                                       tags: %w[latest rc1 pre1])
        end

        it 'should have image tags' do
          expect(project).to have_container_registry_tags
        end
      end

      context 'when there are no tags at all' do
        before do
          stub_container_registry_tags(repository: :any, tags: [])
        end

        it 'should not have image tags' do
          expect(project).not_to have_container_registry_tags
        end
      end
    end

    context 'when container registry is disabled' do
1539 1540 1541
      before do
        stub_container_registry_config(enabled: false)
      end
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558

      it 'should not have image tags' do
        expect(project).not_to have_container_registry_tags
      end

      it 'should not check root repository tags' do
        expect(project).not_to receive(:full_path)
        expect(project).not_to have_container_registry_tags
      end

      it 'should iterate through container repositories' do
        expect(project).to receive(:container_repositories)
        expect(project).not_to have_container_registry_tags
      end
    end
  end

1559
  describe '#ci_config_path=' do
1560
    let(:project) { create(:project) }
1561 1562

    it 'sets nil' do
1563
      project.update!(ci_config_path: nil)
1564

1565
      expect(project.ci_config_path).to be_nil
1566 1567 1568
    end

    it 'sets a string' do
1569
      project.update!(ci_config_path: 'foo/.gitlab_ci.yml')
1570

1571
      expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
1572 1573
    end

1574
    it 'sets a string but removes all leading slashes and null characters' do
1575
      project.update!(ci_config_path: "///f\0oo/\0/.gitlab_ci.yml")
1576

1577
      expect(project.ci_config_path).to eq('foo//.gitlab_ci.yml')
1578 1579 1580
    end
  end

1581
  describe 'Project import job' do
1582
    let(:project) { create(:project, import_url: generate(:url)) }
1583 1584

    before do
1585
      allow_any_instance_of(Gitlab::Shell).to receive(:import_repository)
1586
        .with(project.repository_storage_path, project.disk_path, project.import_url)
1587 1588 1589 1590
        .and_return(true)

      expect_any_instance_of(Repository).to receive(:after_import)
        .and_call_original
1591 1592 1593 1594 1595
    end

    it 'imports a project' do
      expect_any_instance_of(RepositoryImportWorker).to receive(:perform).and_call_original

1596
      expect { project.import_schedule }.to change { project.import_jid }
1597 1598 1599 1600
      expect(project.reload.import_status).to eq('finished')
    end
  end

1601 1602
  describe 'project import state transitions' do
    context 'state transition: [:started] => [:finished]' do
Lin Jen-Shin committed
1603
      let(:after_import_service) { spy(:after_import_service) }
Lin Jen-Shin committed
1604
      let(:housekeeping_service) { spy(:housekeeping_service) }
1605 1606

      before do
Lin Jen-Shin committed
1607 1608
        allow(Projects::AfterImportService)
          .to receive(:new) { after_import_service }
Lin Jen-Shin committed
1609

Lin Jen-Shin committed
1610
        allow(after_import_service)
Lin Jen-Shin committed
1611 1612 1613 1614
          .to receive(:execute) { housekeeping_service.execute }

        allow(Projects::HousekeepingService)
          .to receive(:new) { housekeeping_service }
1615 1616
      end

1617 1618 1619 1620 1621 1622 1623
      it 'resets project import_error' do
        error_message = 'Some error'
        mirror = create(:project_empty_repo, :import_started, import_error: error_message)

        expect { mirror.import_finish }.to change { mirror.import_error }.from(error_message).to(nil)
      end

1624 1625 1626 1627 1628
      it 'performs housekeeping when an import of a fresh project is completed' do
        project = create(:project_empty_repo, :import_started, import_type: :github)

        project.import_finish

Lin Jen-Shin committed
1629
        expect(after_import_service).to have_received(:execute)
1630 1631 1632 1633
        expect(housekeeping_service).to have_received(:execute)
      end

      it 'does not perform housekeeping when project repository does not exist' do
1634
        project = create(:project, :import_started, import_type: :github)
1635 1636 1637 1638 1639 1640 1641

        project.import_finish

        expect(housekeeping_service).not_to have_received(:execute)
      end

      it 'does not perform housekeeping when project does not have a valid import type' do
1642
        project = create(:project, :import_started, import_type: nil)
1643 1644 1645 1646 1647 1648 1649 1650

        project.import_finish

        expect(housekeeping_service).not_to have_received(:execute)
      end
    end
  end

1651
  describe '#latest_successful_builds_for' do
Lin Jen-Shin committed
1652
    def create_pipeline(status = 'success')
1653
      create(:ci_pipeline, project: project,
Lin Jen-Shin committed
1654
                           sha: project.commit.sha,
1655
                           ref: project.default_branch,
Lin Jen-Shin committed
1656
                           status: status)
1657 1658
    end

Lin Jen-Shin committed
1659 1660 1661
    def create_build(new_pipeline = pipeline, name = 'test')
      create(:ci_build, :success, :artifacts,
             pipeline: new_pipeline,
Lin Jen-Shin committed
1662
             status: new_pipeline.status,
Lin Jen-Shin committed
1663
             name: name)
1664 1665
    end

1666
    let(:project) { create(:project, :repository) }
Lin Jen-Shin committed
1667
    let(:pipeline) { create_pipeline }
Lin Jen-Shin committed
1668 1669

    context 'with many builds' do
1670
      it 'gives the latest builds from latest pipeline' do
1671 1672
        pipeline1 = create_pipeline
        pipeline2 = create_pipeline
1673
        build1_p2 = create_build(pipeline2, 'test')
1674 1675
        create_build(pipeline1, 'test')
        create_build(pipeline1, 'test2')
1676
        build2_p2 = create_build(pipeline2, 'test2')
Lin Jen-Shin committed
1677 1678 1679

        latest_builds = project.latest_successful_builds_for

1680
        expect(latest_builds).to contain_exactly(build2_p2, build1_p2)
Lin Jen-Shin committed
1681 1682
      end
    end
Lin Jen-Shin committed
1683

Lin Jen-Shin committed
1684
    context 'with succeeded pipeline' do
Lin Jen-Shin committed
1685
      let!(:build) { create_build }
1686

Lin Jen-Shin committed
1687
      context 'standalone pipeline' do
1688 1689 1690 1691 1692 1693 1694 1695
        it 'returns builds for ref for default_branch' do
          builds = project.latest_successful_builds_for

          expect(builds).to contain_exactly(build)
        end

        it 'returns empty relation if the build cannot be found' do
          builds = project.latest_successful_builds_for('TAIL')
1696

1697 1698 1699
          expect(builds).to be_kind_of(ActiveRecord::Relation)
          expect(builds).to be_empty
        end
1700 1701
      end

Lin Jen-Shin committed
1702
      context 'with some pending pipeline' do
1703
        before do
Lin Jen-Shin committed
1704
          create_build(create_pipeline('pending'))
1705 1706
        end

Lin Jen-Shin committed
1707 1708
        it 'gives the latest build from latest pipeline' do
          latest_build = project.latest_successful_builds_for
1709

Lin Jen-Shin committed
1710
          expect(latest_build).to contain_exactly(build)
1711
        end
1712 1713 1714 1715 1716 1717
      end
    end

    context 'with pending pipeline' do
      before do
        pipeline.update(status: 'pending')
1718
        create_build(pipeline)
1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729
      end

      it 'returns empty relation' do
        builds = project.latest_successful_builds_for

        expect(builds).to be_kind_of(ActiveRecord::Relation)
        expect(builds).to be_empty
      end
    end
  end

1730
  describe '#add_import_job' do
1731 1732
    let(:import_jid) { '123' }

1733
    context 'forked' do
1734
      let(:forked_project_link) { create(:forked_project_link, :forked_to_empty_project) }
1735 1736 1737 1738
      let(:forked_from_project) { forked_project_link.forked_from_project }
      let(:project) { forked_project_link.forked_to_project }

      it 'schedules a RepositoryForkWorker job' do
1739 1740 1741 1742 1743
        expect(RepositoryForkWorker).to receive(:perform_async).with(
          project.id,
          forked_from_project.repository_storage_path,
          forked_from_project.disk_path,
          project.namespace.full_path).and_return(import_jid)
1744

1745
        expect(project.add_import_job).to eq(import_jid)
1746 1747 1748 1749 1750
      end
    end

    context 'not forked' do
      it 'schedules a RepositoryImportWorker job' do
1751
        project = create(:project, import_url: generate(:url))
1752

1753 1754
        expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
        expect(project.add_import_job).to eq(import_jid)
1755 1756 1757 1758
      end
    end
  end

1759
  describe '#gitlab_project_import?' do
1760
    subject(:project) { build(:project, import_type: 'gitlab_project') }
1761 1762 1763 1764 1765

    it { expect(project.gitlab_project_import?).to be true }
  end

  describe '#gitea_import?' do
1766
    subject(:project) { build(:project, import_type: 'gitea') }
1767 1768 1769 1770

    it { expect(project.gitea_import?).to be true }
  end

1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
  describe '#ancestors_upto', :nested_groups do
    let(:parent) { create(:group) }
    let(:child) { create(:group, parent: parent) }
    let(:child2) { create(:group, parent: child) }
    let(:project) { create(:project, namespace: child2) }

    it 'returns all ancestors when no namespace is given' do
      expect(project.ancestors_upto).to contain_exactly(child2, child, parent)
    end

    it 'includes ancestors upto but excluding the given ancestor' do
      expect(project.ancestors_upto(parent)).to contain_exactly(child2, child)
    end
  end

1786
  describe '#lfs_enabled?' do
1787
    let(:project) { create(:project) }
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847

    shared_examples 'project overrides group' do
      it 'returns true when enabled in project' do
        project.update_attribute(:lfs_enabled, true)

        expect(project.lfs_enabled?).to be_truthy
      end

      it 'returns false when disabled in project' do
        project.update_attribute(:lfs_enabled, false)

        expect(project.lfs_enabled?).to be_falsey
      end

      it 'returns the value from the namespace, when no value is set in project' do
        expect(project.lfs_enabled?).to eq(project.namespace.lfs_enabled?)
      end
    end

    context 'LFS disabled in group' do
      before do
        project.namespace.update_attribute(:lfs_enabled, false)
        enable_lfs
      end

      it_behaves_like 'project overrides group'
    end

    context 'LFS enabled in group' do
      before do
        project.namespace.update_attribute(:lfs_enabled, true)
        enable_lfs
      end

      it_behaves_like 'project overrides group'
    end

    describe 'LFS disabled globally' do
      shared_examples 'it always returns false' do
        it do
          expect(project.lfs_enabled?).to be_falsey
          expect(project.namespace.lfs_enabled?).to be_falsey
        end
      end

      context 'when no values are set' do
        it_behaves_like 'it always returns false'
      end

      context 'when all values are set to true' do
        before do
          project.namespace.update_attribute(:lfs_enabled, true)
          project.update_attribute(:lfs_enabled, true)
        end

        it_behaves_like 'it always returns false'
      end
    end
  end

1848
  describe '#change_head' do
1849
    let(:project) { create(:project, :repository) }
1850

1851 1852 1853 1854 1855
    it 'returns error if branch does not exist' do
      expect(project.change_head('unexisted-branch')).to be false
      expect(project.errors.size).to eq(1)
    end

1856
    it 'calls the before_change_head and after_change_head methods' do
1857
      expect(project.repository).to receive(:before_change_head)
1858 1859
      expect(project.repository).to receive(:after_change_head)

1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879
      project.change_head(project.default_branch)
    end

    it 'creates the new reference with rugged' do
      expect(project.repository.rugged.references).to receive(:create).with('HEAD',
                                                                            "refs/heads/#{project.default_branch}",
                                                                            force: true)
      project.change_head(project.default_branch)
    end

    it 'copies the gitattributes' do
      expect(project.repository).to receive(:copy_gitattributes).with(project.default_branch)
      project.change_head(project.default_branch)
    end

    it 'reloads the default branch' do
      expect(project).to receive(:reload_default_branch)
      project.change_head(project.default_branch)
    end
  end
1880

1881
  context 'forks' do
1882 1883
    include ProjectForksHelper

1884 1885 1886 1887 1888
    let(:project) { create(:project, :public) }
    let!(:forked_project) { fork_project(project) }

    describe '#fork_network' do
      it 'includes a fork of the project' do
1889
        expect(project.fork_network.projects).to include(forked_project)
1890 1891 1892 1893 1894
      end

      it 'includes a fork of a fork' do
        other_fork = fork_project(forked_project)

1895
        expect(project.fork_network.projects).to include(other_fork)
1896 1897 1898 1899 1900
      end

      it 'includes sibling forks' do
        other_fork = fork_project(project)

1901
        expect(forked_project.fork_network.projects).to include(other_fork)
1902 1903 1904
      end

      it 'includes the base project' do
1905
        expect(forked_project.fork_network.projects).to include(project.reload)
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931
      end
    end

    describe '#in_fork_network_of?' do
      it 'is true for a real fork' do
        expect(forked_project.in_fork_network_of?(project)).to be_truthy
      end

      it 'is true for a fork of a fork', :postgresql do
        other_fork = fork_project(forked_project)

        expect(other_fork.in_fork_network_of?(project)).to be_truthy
      end

      it 'is true for sibling forks' do
        sibling = fork_project(project)

        expect(sibling.in_fork_network_of?(forked_project)).to be_truthy
      end

      it 'is false when another project is given' do
        other_project = build_stubbed(:project)

        expect(forked_project.in_fork_network_of?(other_project)).to be_falsy
      end
    end
1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945

    describe '#fork_source' do
      let!(:second_fork) { fork_project(forked_project) }

      it 'returns the direct source if it exists' do
        expect(second_fork.fork_source).to eq(forked_project)
      end

      it 'returns the root of the fork network when the directs source was deleted' do
        forked_project.destroy

        expect(second_fork.fork_source).to eq(project)
      end
    end
1946 1947
  end

1948
  describe '#pushes_since_gc' do
1949
    let(:project) { create(:project) }
1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970

    after do
      project.reset_pushes_since_gc
    end

    context 'without any pushes' do
      it 'returns 0' do
        expect(project.pushes_since_gc).to eq(0)
      end
    end

    context 'with a number of pushes' do
      it 'returns the number of pushes' do
        3.times { project.increment_pushes_since_gc }

        expect(project.pushes_since_gc).to eq(3)
      end
    end
  end

  describe '#increment_pushes_since_gc' do
1971
    let(:project) { create(:project) }
1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984

    after do
      project.reset_pushes_since_gc
    end

    it 'increments the number of pushes since the last GC' do
      3.times { project.increment_pushes_since_gc }

      expect(project.pushes_since_gc).to eq(3)
    end
  end

  describe '#reset_pushes_since_gc' do
1985
    let(:project) { create(:project) }
1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998

    after do
      project.reset_pushes_since_gc
    end

    it 'resets the number of pushes since the last GC' do
      3.times { project.increment_pushes_since_gc }

      project.reset_pushes_since_gc

      expect(project.pushes_since_gc).to eq(0)
    end
  end
1999

2000 2001
  describe '#deployment_variables' do
    context 'when project has no deployment service' do
2002
      let(:project) { create(:project) }
2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019

      it 'returns an empty array' do
        expect(project.deployment_variables).to eq []
      end
    end

    context 'when project has a deployment service' do
      let(:project) { create(:kubernetes_project) }

      it 'returns variables from this service' do
        expect(project.deployment_variables).to include(
          { key: 'KUBE_TOKEN', value: project.kubernetes_service.token, public: false }
        )
      end
    end
  end

2020
  describe '#secret_variables_for' do
2021
    let(:project) { create(:project) }
2022 2023 2024 2025 2026 2027 2028 2029 2030

    let!(:secret_variable) do
      create(:ci_variable, value: 'secret', project: project)
    end

    let!(:protected_variable) do
      create(:ci_variable, :protected, value: 'protected', project: project)
    end

Lin Jen-Shin committed
2031 2032 2033 2034 2035 2036
    subject { project.secret_variables_for(ref: 'ref') }

    before do
      stub_application_setting(
        default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end
2037 2038 2039

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
2040
        is_expected.to contain_exactly(secret_variable, protected_variable)
2041 2042 2043 2044
      end
    end

    context 'when the ref is not protected' do
2045
      it 'contains only the secret variables' do
2046
        is_expected.to contain_exactly(secret_variable)
2047 2048 2049
      end
    end

2050 2051 2052
    context 'when the ref is a protected branch' do
      before do
        create(:protected_branch, name: 'ref', project: project)
2053
      end
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063

      it_behaves_like 'ref is protected'
    end

    context 'when the ref is a protected tag' do
      before do
        create(:protected_tag, name: 'ref', project: project)
      end

      it_behaves_like 'ref is protected'
2064 2065 2066
    end
  end

2067
  describe '#protected_for?' do
2068
    let(:project) { create(:project) }
2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103

    subject { project.protected_for?('ref') }

    context 'when the ref is not protected' do
      before do
        stub_application_setting(
          default_branch_protection: Gitlab::Access::PROTECTION_NONE)
      end

      it 'returns false' do
        is_expected.to be_falsey
      end
    end

    context 'when the ref is a protected branch' do
      before do
        create(:protected_branch, name: 'ref', project: project)
      end

      it 'returns true' do
        is_expected.to be_truthy
      end
    end

    context 'when the ref is a protected tag' do
      before do
        create(:protected_tag, name: 'ref', project: project)
      end

      it 'returns true' do
        is_expected.to be_truthy
      end
    end
  end

2104
  describe '#update_project_statistics' do
2105
    let(:project) { create(:project) }
2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123

    it "is called after creation" do
      expect(project.statistics).to be_a ProjectStatistics
      expect(project.statistics).to be_persisted
    end

    it "copies the namespace_id" do
      expect(project.statistics.namespace_id).to eq project.namespace_id
    end

    it "updates the namespace_id when changed" do
      namespace = create(:namespace)
      project.update(namespace: namespace)

      expect(project.statistics.namespace_id).to eq namespace.id
    end
  end

2124
  describe 'inside_path' do
2125 2126 2127
    let!(:project1) { create(:project, namespace: create(:namespace, path: 'name_pace')) }
    let!(:project2) { create(:project) }
    let!(:project3) { create(:project, namespace: create(:namespace, path: 'namespace')) }
2128
    let!(:path) { project1.namespace.full_path }
2129

2130
    it 'returns correct project' do
2131
      expect(described_class.inside_path(path)).to eq([project1])
2132
    end
2133 2134
  end

Douwe Maan committed
2135
  describe '#route_map_for' do
2136
    let(:project) { create(:project, :repository) }
Douwe Maan committed
2137 2138 2139 2140 2141 2142 2143 2144
    let(:route_map) do
      <<-MAP.strip_heredoc
      - source: /source/(.*)/
        public: '\\1'
      MAP
    end

    before do
2145
      project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
Douwe Maan committed
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172
    end

    context 'when there is a .gitlab/route-map.yml at the commit' do
      context 'when the route map is valid' do
        it 'returns a route map' do
          map = project.route_map_for(project.commit.sha)
          expect(map).to be_a_kind_of(Gitlab::RouteMap)
        end
      end

      context 'when the route map is invalid' do
        let(:route_map) { 'INVALID' }

        it 'returns nil' do
          expect(project.route_map_for(project.commit.sha)).to be_nil
        end
      end
    end

    context 'when there is no .gitlab/route-map.yml at the commit' do
      it 'returns nil' do
        expect(project.route_map_for(project.commit.parent.sha)).to be_nil
      end
    end
  end

  describe '#public_path_for_source_path' do
2173
    let(:project) { create(:project, :repository) }
Douwe Maan committed
2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210
    let(:route_map) do
      Gitlab::RouteMap.new(<<-MAP.strip_heredoc)
        - source: /source/(.*)/
          public: '\\1'
      MAP
    end
    let(:sha) { project.commit.id }

    context 'when there is a route map' do
      before do
        allow(project).to receive(:route_map_for).with(sha).and_return(route_map)
      end

      context 'when the source path is mapped' do
        it 'returns the public path' do
          expect(project.public_path_for_source_path('source/file.html', sha)).to eq('file.html')
        end
      end

      context 'when the source path is not mapped' do
        it 'returns nil' do
          expect(project.public_path_for_source_path('file.html', sha)).to be_nil
        end
      end
    end

    context 'when there is no route map' do
      before do
        allow(project).to receive(:route_map_for).with(sha).and_return(nil)
      end

      it 'returns nil' do
        expect(project.public_path_for_source_path('source/file.html', sha)).to be_nil
      end
    end
  end

2211
  describe '#parent' do
2212
    let(:project) { create(:project) }
2213 2214 2215 2216

    it { expect(project.parent).to eq(project.namespace) }
  end

2217 2218 2219 2220 2221 2222
  describe '#parent_id' do
    let(:project) { create(:project) }

    it { expect(project.parent_id).to eq(project.namespace_id) }
  end

2223
  describe '#parent_changed?' do
2224
    let(:project) { create(:project) }
2225

2226 2227 2228
    before do
      project.namespace_id = 7
    end
2229 2230 2231 2232

    it { expect(project.parent_changed?).to be_truthy }
  end

2233 2234 2235
  def enable_lfs
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
  end
2236

2237
  describe '#pages_url' do
2238 2239
    let(:group) { create :group, name: 'Group' }
    let(:nested_group) { create :group, parent: group }
2240 2241 2242 2243 2244 2245 2246 2247 2248
    let(:domain) { 'Example.com' }

    subject { project.pages_url }

    before do
      allow(Settings.pages).to receive(:host).and_return(domain)
      allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com')
    end

2249
    context 'top-level group' do
2250
      let(:project) { create :project, namespace: group, name: project_name }
2251

2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262
      context 'group page' do
        let(:project_name) { 'group.example.com' }

        it { is_expected.to eq("http://group.example.com") }
      end

      context 'project page' do
        let(:project_name) { 'Project' }

        it { is_expected.to eq("http://group.example.com/project") }
      end
2263 2264
    end

2265
    context 'nested group' do
2266
      let(:project) { create :project, namespace: nested_group, name: project_name }
2267
      let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" }
2268

2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
      context 'group page' do
        let(:project_name) { 'group.example.com' }

        it { is_expected.to eq(expected_url) }
      end

      context 'project page' do
        let(:project_name) { 'Project' }

        it { is_expected.to eq(expected_url) }
      end
2280 2281
    end
  end
2282 2283

  describe '#http_url_to_repo' do
2284
    let(:project) { create :project }
2285

2286 2287 2288
    it 'returns the url to the repo without a username' do
      expect(project.http_url_to_repo).to eq("#{project.web_url}.git")
      expect(project.http_url_to_repo).not_to include('@')
2289 2290
    end
  end
2291 2292

  describe '#pipeline_status' do
2293
    let(:project) { create(:project, :repository) }
2294
    it 'builds a pipeline status' do
2295
      expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
2296 2297 2298 2299 2300 2301
    end

    it 'hase a loaded pipeline status' do
      expect(project.pipeline_status).to be_loaded
    end
  end
2302 2303

  describe '#append_or_update_attribute' do
2304
    let(:project) { create(:project) }
2305 2306 2307 2308 2309

    it 'shows full error updating an invalid MR' do
      error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\
                      ' Validate fork Source project is not a fork of the target project'

2310 2311
      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
        .to raise_error(ActiveRecord::RecordNotSaved, error_message)
2312 2313 2314 2315 2316
    end

    it 'updates the project succesfully' do
      merge_request = create(:merge_request, target_project: project, source_project: project)

2317 2318
      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
        .not_to raise_error
2319 2320
    end
  end
2321 2322 2323

  describe '#last_repository_updated_at' do
    it 'sets to created_at upon creation' do
2324
      project = create(:project, created_at: 2.hours.ago)
2325 2326 2327 2328

      expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
    end
  end
2329 2330 2331 2332 2333

  describe '.public_or_visible_to_user' do
    let!(:user) { create(:user) }

    let!(:private_project) do
2334
      create(:project, :private, creator: user, namespace: user.namespace)
2335 2336
    end

2337
    let!(:public_project) { create(:project, :public) }
2338 2339 2340

    context 'with a user' do
      let(:projects) do
2341
        described_class.all.public_or_visible_to_user(user)
2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354
      end

      it 'includes projects the user has access to' do
        expect(projects).to include(private_project)
      end

      it 'includes projects the user can see' do
        expect(projects).to include(public_project)
      end
    end

    context 'without a user' do
      it 'only includes public projects' do
2355
        projects = described_class.all.public_or_visible_to_user
2356 2357 2358 2359 2360

        expect(projects).to eq([public_project])
      end
    end
  end
2361

2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383
  describe '#pages_available?' do
    let(:project) { create(:project, group: group) }

    subject { project.pages_available? }

    before do
      allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
    end

    context 'when the project is in a top level namespace' do
      let(:group) { create(:group) }

      it { is_expected.to be(true) }
    end

    context 'when the project is in a subgroup' do
      let(:group) { create(:group, :nested) }

      it { is_expected.to be(false) }
    end
  end

2384
  describe '#remove_private_deploy_keys' do
2385
    let!(:project) { create(:project) }
2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400

    context 'for a private deploy key' do
      let!(:key) { create(:deploy_key, public: false) }
      let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) }

      context 'when the key is not linked to another project' do
        it 'removes the key' do
          project.remove_private_deploy_keys

          expect(project.deploy_keys).not_to include(key)
        end
      end

      context 'when the key is linked to another project' do
        before do
2401
          another_project = create(:project)
2402 2403
          create(:deploy_keys_project, deploy_key: key, project: another_project)
        end
2404

2405 2406
        it 'does not remove the key' do
          project.remove_private_deploy_keys
2407

2408 2409 2410 2411 2412 2413 2414 2415
          expect(project.deploy_keys).to include(key)
        end
      end
    end

    context 'for a public deploy key' do
      let!(:key) { create(:deploy_key, public: true) }
      let!(:deploy_keys_project) { create(:deploy_keys_project, deploy_key: key, project: project) }
2416

2417 2418
      it 'does not remove the key' do
        project.remove_private_deploy_keys
2419

2420 2421
        expect(project.deploy_keys).to include(key)
      end
2422 2423
    end
  end
2424

2425 2426
  describe '#remove_pages' do
    let(:project) { create(:project) }
2427
    let(:namespace) { project.namespace }
2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438
    let(:pages_path) { project.pages_path }

    around do |example|
      FileUtils.mkdir_p(pages_path)
      begin
        example.run
      ensure
        FileUtils.rm_rf(pages_path)
      end
    end

2439 2440 2441 2442 2443 2444 2445 2446
    it 'removes the pages directory' do
      expect_any_instance_of(Projects::UpdatePagesConfigurationService).to receive(:execute)
      expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return(true)
      expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, namespace.full_path, anything)

      project.remove_pages
    end

2447 2448 2449 2450 2451 2452 2453 2454
    it 'is a no-op when there is no namespace' do
      project.update_column(:namespace_id, nil)

      expect_any_instance_of(Projects::UpdatePagesConfigurationService).not_to receive(:execute)
      expect_any_instance_of(Gitlab::PagesTransfer).not_to receive(:rename_project)

      project.remove_pages
    end
2455 2456 2457 2458 2459 2460

    it 'is run when the project is destroyed' do
      expect(project).to receive(:remove_pages).and_call_original

      project.destroy
    end
2461 2462
  end

2463 2464 2465 2466 2467 2468 2469 2470 2471
  describe '#forks_count' do
    it 'returns the number of forks' do
      project = build(:project)

      allow(project.forks).to receive(:count).and_return(1)

      expect(project.forks_count).to eq(1)
    end
  end
2472 2473 2474 2475

  context 'legacy storage' do
    let(:project) { create(:project, :repository) }
    let(:gitlab_shell) { Gitlab::Shell.new }
2476
    let(:project_storage) { project.send(:storage) }
2477

2478 2479 2480 2481
    before do
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
    end

2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
    describe '#base_dir' do
      it 'returns base_dir based on namespace only' do
        expect(project.base_dir).to eq(project.namespace.full_path)
      end
    end

    describe '#disk_path' do
      it 'returns disk_path based on namespace and project path' do
        expect(project.disk_path).to eq("#{project.namespace.full_path}/#{project.path}")
      end
    end

2494
    describe '#ensure_storage_path_exists' do
2495 2496 2497
      it 'delegates to gitlab_shell to ensure namespace is created' do
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, project.base_dir)

2498
        project.ensure_storage_path_exists
2499 2500 2501
      end
    end

2502 2503
    describe '#legacy_storage?' do
      it 'returns true when storage_version is nil' do
2504
        project = build(:project, storage_version: nil)
2505 2506 2507

        expect(project.legacy_storage?).to be_truthy
      end
2508 2509 2510 2511 2512 2513 2514 2515 2516 2517

      it 'returns true when the storage_version is 0' do
        project = build(:project, storage_version: 0)

        expect(project.legacy_storage?).to be_truthy
      end
    end

    describe '#hashed_storage?' do
      it 'returns false' do
2518
        expect(project.hashed_storage?(:repository)).to be_falsey
2519
      end
2520 2521
    end

2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568
    describe '#rename_repo' do
      before do
        # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
        # call. This makes testing a bit easier.
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
        allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
      end

      it 'renames a repository' do
        stub_container_registry_config(enabled: false)

        expect(gitlab_shell).to receive(:mv_repository)
          .ordered
          .with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}")
          .and_return(true)

        expect(gitlab_shell).to receive(:mv_repository)
          .ordered
          .with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki")
          .and_return(true)

        expect_any_instance_of(SystemHooksService)
          .to receive(:execute_hooks_for)
            .with(project, :rename)

        expect_any_instance_of(Gitlab::UploadsTransfer)
          .to receive(:rename_project)
            .with('foo', project.path, project.namespace.full_path)

        expect(project).to receive(:expire_caches_before_rename)

        expect(project).to receive(:expires_full_path_cache)

        project.rename_repo
      end

      context 'container registry with images' do
        let(:container_repository) { create(:container_repository) }

        before do
          stub_container_registry_config(enabled: true)
          stub_container_registry_tags(repository: :any, tags: ['tag'])
          project.container_repositories << container_repository
        end

        subject { project.rename_repo }

2569
        it { expect { subject }.to raise_error(StandardError) }
2570
      end
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594

      context 'gitlab pages' do
        before do
          expect(project_storage).to receive(:rename_repo) { true }
        end

        it 'moves pages folder to new location' do
          expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)

          project.rename_repo
        end
      end

      context 'attachments' do
        before do
          expect(project_storage).to receive(:rename_repo) { true }
        end

        it 'moves uploads folder to new location' do
          expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)

          project.rename_repo
        end
      end
2595
    end
2596 2597 2598 2599 2600 2601

    describe '#pages_path' do
      it 'returns a path where pages are stored' do
        expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path))
      end
    end
2602 2603 2604 2605 2606 2607

    describe '#migrate_to_hashed_storage!' do
      it 'returns true' do
        expect(project.migrate_to_hashed_storage!).to be_truthy
      end

2608
      it 'flags as read-only' do
2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633
        expect { project.migrate_to_hashed_storage! }.to change { project.repository_read_only }.to(true)
      end

      it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do
        Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase

        expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in)

        project.migrate_to_hashed_storage!
      end

      it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the wiki repo is in use' do
        Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: true)).increase

        expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in)

        project.migrate_to_hashed_storage!
      end

      it 'schedules ProjectMigrateHashedStorageWorker' do
        expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).with(project.id)

        project.migrate_to_hashed_storage!
      end
    end
2634 2635 2636
  end

  context 'hashed storage' do
2637
    let(:project) { create(:project, :repository) }
2638 2639 2640 2641
    let(:gitlab_shell) { Gitlab::Shell.new }
    let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }

    before do
2642
      stub_application_setting(hashed_storage_enabled: true)
2643
      allow(Digest::SHA2).to receive(:hexdigest) { hash }
2644
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
2645 2646
    end

2647 2648 2649 2650 2651 2652 2653
    describe '#legacy_storage?' do
      it 'returns false' do
        expect(project.legacy_storage?).to be_falsey
      end
    end

    describe '#hashed_storage?' do
2654 2655
      it 'returns true if rolled out' do
        expect(project.hashed_storage?(:attachments)).to be_truthy
2656 2657
      end

2658 2659
      it 'returns false when not rolled out yet' do
        project.storage_version = 1
2660

2661
        expect(project.hashed_storage?(:attachments)).to be_falsey
2662 2663 2664
      end
    end

2665 2666
    describe '#base_dir' do
      it 'returns base_dir based on hash of project id' do
2667
        expect(project.base_dir).to eq('@hashed/6b/86')
2668 2669 2670 2671
      end
    end

    describe '#disk_path' do
2672
      it 'returns disk_path based on hash of project id' do
2673
        hashed_path = '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b'
2674 2675 2676 2677 2678

        expect(project.disk_path).to eq(hashed_path)
      end
    end

2679
    describe '#ensure_storage_path_exists' do
2680
      it 'delegates to gitlab_shell to ensure namespace is created' do
2681
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, '@hashed/6b/86')
2682

2683
        project.ensure_storage_path_exists
2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721
      end
    end

    describe '#rename_repo' do
      before do
        # Project#gitlab_shell returns a new instance of Gitlab::Shell on every
        # call. This makes testing a bit easier.
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
        allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
      end

      it 'renames a repository' do
        stub_container_registry_config(enabled: false)

        expect(gitlab_shell).not_to receive(:mv_repository)

        expect_any_instance_of(SystemHooksService)
          .to receive(:execute_hooks_for)
            .with(project, :rename)

        expect(project).to receive(:expire_caches_before_rename)

        expect(project).to receive(:expires_full_path_cache)

        project.rename_repo
      end

      context 'container registry with images' do
        let(:container_repository) { create(:container_repository) }

        before do
          stub_container_registry_config(enabled: true)
          stub_container_registry_tags(repository: :any, tags: ['tag'])
          project.container_repositories << container_repository
        end

        subject { project.rename_repo }

2722
        it { expect { subject }.to raise_error(StandardError) }
2723
      end
2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749

      context 'gitlab pages' do
        it 'moves pages folder to new location' do
          expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)

          project.rename_repo
        end
      end

      context 'attachments' do
        it 'keeps uploads folder location unchanged' do
          expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project)

          project.rename_repo
        end

        context 'when not rolled out' do
          let(:project) { create(:project, :repository, storage_version: 1) }

          it 'moves pages folder to new location' do
            expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)

            project.rename_repo
          end
        end
      end
2750
    end
2751 2752 2753 2754 2755 2756

    describe '#pages_path' do
      it 'returns a path where pages are stored' do
        expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path))
      end
    end
2757 2758 2759 2760 2761 2762

    describe '#migrate_to_hashed_storage!' do
      it 'returns nil' do
        expect(project.migrate_to_hashed_storage!).to be_nil
      end

2763
      it 'does not flag as read-only' do
2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776
        expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only }
      end
    end
  end

  describe '#gl_repository' do
    let(:project) { create(:project) }

    it 'delegates to Gitlab::GlRepository.gl_repository' do
      expect(Gitlab::GlRepository).to receive(:gl_repository).with(project, true)

      project.gl_repository(is_wiki: true)
    end
2777
  end
2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875

  describe '#has_ci?' do
    set(:project) { create(:project) }
    let(:repository) { double }

    before do
      expect(project).to receive(:repository) { repository }
    end

    context 'when has .gitlab-ci.yml' do
      before do
        expect(repository).to receive(:gitlab_ci_yml) { 'content' }
      end

      it "CI is available" do
        expect(project).to have_ci
      end
    end

    context 'when there is no .gitlab-ci.yml' do
      before do
        expect(repository).to receive(:gitlab_ci_yml) { nil }
      end

      it "CI is not available" do
        expect(project).not_to have_ci
      end

      context 'when auto devops is enabled' do
        before do
          stub_application_setting(auto_devops_enabled: true)
        end

        it "CI is available" do
          expect(project).to have_ci
        end
      end
    end
  end

  describe '#auto_devops_enabled?' do
    set(:project) { create(:project) }

    subject { project.auto_devops_enabled? }

    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

      it 'auto devops is implicitly enabled' do
        expect(project.auto_devops).to be_nil
        expect(project).to be_auto_devops_enabled
      end

      context 'when explicitly enabled' do
        before do
          create(:project_auto_devops, project: project)
        end

        it "auto devops is enabled" do
          expect(project).to be_auto_devops_enabled
        end
      end

      context 'when explicitly disabled' do
        before do
          create(:project_auto_devops, project: project, enabled: false)
        end

        it "auto devops is disabled" do
          expect(project).not_to be_auto_devops_enabled
        end
      end
    end

    context 'when disabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: false)
      end

      it 'auto devops is implicitly disabled' do
        expect(project.auto_devops).to be_nil
        expect(project).not_to be_auto_devops_enabled
      end

      context 'when explicitly enabled' do
        before do
          create(:project_auto_devops, project: project)
        end

        it "auto devops is enabled" do
          expect(project).to be_auto_devops_enabled
        end
      end
    end
  end

2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919
  describe '#has_auto_devops_implicitly_disabled?' do
    set(:project) { create(:project) }

    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

      it 'does not have auto devops implicitly disabled' do
        expect(project).not_to have_auto_devops_implicitly_disabled
      end
    end

    context 'when disabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: false)
      end

      it 'auto devops is implicitly disabled' do
        expect(project).to have_auto_devops_implicitly_disabled
      end

      context 'when explicitly disabled' do
        before do
          create(:project_auto_devops, project: project, enabled: false)
        end

        it 'does not have auto devops implicitly disabled' do
          expect(project).not_to have_auto_devops_implicitly_disabled
        end
      end

      context 'when explicitly enabled' do
        before do
          create(:project_auto_devops, project: project)
        end

        it 'does not have auto devops implicitly disabled' do
          expect(project).not_to have_auto_devops_implicitly_disabled
        end
      end
    end
  end

2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950
  context '#auto_devops_variables' do
    set(:project) { create(:project) }

    subject { project.auto_devops_variables }

    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

      context 'when domain is empty' do
        before do
          create(:project_auto_devops, project: project, domain: nil)
        end

        it 'variables are empty' do
          is_expected.to be_empty
        end
      end

      context 'when domain is configured' do
        before do
          create(:project_auto_devops, project: project, domain: 'example.com')
        end

        it "variables are not empty" do
          is_expected.not_to be_empty
        end
      end
    end
  end
2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985

  describe '#latest_successful_builds_for' do
    let(:project) { build(:project) }

    before do
      allow(project).to receive(:default_branch).and_return('master')
    end

    context 'without a ref' do
      it 'returns a pipeline for the default branch' do
        expect(project)
          .to receive(:latest_successful_pipeline_for_default_branch)

        project.latest_successful_pipeline_for
      end
    end

    context 'with the ref set to the default branch' do
      it 'returns a pipeline for the default branch' do
        expect(project)
          .to receive(:latest_successful_pipeline_for_default_branch)

        project.latest_successful_pipeline_for(project.default_branch)
      end
    end

    context 'with a ref that is not the default branch' do
      it 'returns the latest successful pipeline for the given ref' do
        expect(project.pipelines).to receive(:latest_successful_for).with('foo')

        project.latest_successful_pipeline_for('foo')
      end
    end
  end

2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996
  describe '#check_repository_path_availability' do
    let(:project) { build(:project) }

    it 'skips gitlab-shell exists?' do
      project.skip_disk_validation = true

      expect(project.gitlab_shell).not_to receive(:exists?)
      expect(project.check_repository_path_availability).to be_truthy
    end
  end

2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017
  describe '#latest_successful_pipeline_for_default_branch' do
    let(:project) { build(:project) }

    before do
      allow(project).to receive(:default_branch).and_return('master')
    end

    it 'memoizes and returns the latest successful pipeline for the default branch' do
      pipeline = double(:pipeline)

      expect(project.pipelines).to receive(:latest_successful_for)
        .with(project.default_branch)
        .and_return(pipeline)
        .once

      2.times do
        expect(project.latest_successful_pipeline_for_default_branch)
          .to eq(pipeline)
      end
    end
  end
gitlabhq committed
3018
end