project_spec.rb 89.5 KB
Newer Older
G
gitlabhq 已提交
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) }
U
ubudzisz 已提交
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 27 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
    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) }
    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') }
U
ubudzisz 已提交
54 55
    it { is_expected.to have_one(:last_event).class_name('Event') }
    it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
Z
Zeger-Jan van de Weg 已提交
56
    it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
K
Kamil Trzcinski 已提交
57
    it { is_expected.to have_many(:commit_statuses) }
58
    it { is_expected.to have_many(:pipelines) }
59
    it { is_expected.to have_many(:builds) }
60
    it { is_expected.to have_many(:build_trace_section_names)}
61 62
    it { is_expected.to have_many(:runner_projects) }
    it { is_expected.to have_many(:runners) }
K
Kamil Trzcinski 已提交
63
    it { is_expected.to have_many(:active_runners) }
64 65
    it { is_expected.to have_many(:variables) }
    it { is_expected.to have_many(:triggers) }
K
Kamil Trzcinski 已提交
66
    it { is_expected.to have_many(:pages_domains) }
67 68 69 70 71 72 73 74 75
    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) }
U
ubudzisz 已提交
76
    it { is_expected.to have_many(:forks).through(:forked_project_links) }
77
    it { is_expected.to have_many(:uploads).dependent(:destroy) }
78
    it { is_expected.to have_many(:pipeline_schedules) }
79
    it { is_expected.to have_many(:members_and_requesters) }
K
Kamil Trzcinski 已提交
80
    it { is_expected.to have_one(:cluster) }
81

F
Felipe Artur 已提交
82 83
    context 'after initialized' do
      it "has a project_feature" do
84
        expect(described_class.new.project_feature).to be_present
85 86 87
      end
    end

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

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

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

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

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

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

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

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

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

136 137 138
    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) }
139

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

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

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

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

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

          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
164
        it 'has an error on the name attribute' do
165 166
          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
167 168 169 170 171 172

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

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

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

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

188
    it 'does not allow an invalid URI as import_url' do
189
      project2 = build(:project, import_url: 'invalid://')
J
James Lopez 已提交
190 191 192 193

      expect(project2).not_to be_valid
    end

194
    it 'does allow a valid URI as import_url' do
195
      project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
J
James Lopez 已提交
196

197 198
      expect(project2).to be_valid
    end
199

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

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

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

      expect(project2.import_data).to be_nil
    end

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

      expect(project2.import_data).to be_nil
    end
217

D
Douwe Maan 已提交
218
    it "does not allow blocked import_url localhost" do
219
      project2 = build(:project, import_url: 'http://localhost:9000/t.git')
D
Douwe Maan 已提交
220 221 222 223 224 225

      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
226
      project2 = build(:project, import_url: 'http://github.com:25/t.git')
D
Douwe Maan 已提交
227 228 229 230 231

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

232 233
    describe 'project pending deletion' do
      let!(:project_pending_deletion) do
234
        create(:project,
235 236 237
               pending_delete: true)
      end
      let(:new_project) do
238
        build(:project,
239 240 241 242 243 244 245 246 247 248 249 250
              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
251 252 253

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

        expect(project).to be_valid
      end

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

        expect(project).not_to be_valid
      end
264 265 266

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

        expect(project).not_to be_valid
      end
271 272 273

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

        expect(project).to be_valid
      end
278
    end
G
gitlabhq 已提交
279
  end
280

K
Kamil Trzcinski 已提交
281
  describe 'project token' do
282
    it 'sets an random token if none provided' do
283
      project = FactoryGirl.create :project, runners_token: ''
K
Kamil Trzcinski 已提交
284
      expect(project.runners_token).not_to eq('')
K
Kamil Trzcinski 已提交
285 286
    end

U
ubudzisz 已提交
287
    it 'does not set an random token if one provided' do
288
      project = FactoryGirl.create :project, runners_token: 'my-token'
K
Kamil Trzcinski 已提交
289
      expect(project.runners_token).to eq('my-token')
K
Kamil Trzcinski 已提交
290 291
    end
  end
G
gitlabhq 已提交
292

293
  describe 'Respond to' do
294 295 296 297 298
    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) }
299
    it { is_expected.to respond_to(:full_path) }
G
gitlabhq 已提交
300 301
  end

302
  describe 'delegation' do
303 304 305 306 307 308 309 310
    [: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) }
311 312
  end

313
  describe '#to_reference' do
314
    let(:owner)     { create(:user, name: 'Gitlab') }
315
    let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
316
    let(:project)   { create(:project, path: 'sample-project', namespace: namespace) }
317
    let(:group)     { create(:group, name: 'Group', path: 'sample-group', owner: owner) }
318

319
    context 'when nil argument' do
320 321 322 323 324
      it 'returns nil' do
        expect(project.to_reference).to be_nil
      end
    end

325
    context 'when full is true' do
326
      it 'returns complete path to the project' do
327 328 329
        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'
330 331 332 333 334 335 336 337 338 339
      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
340
      let(:another_namespace_project) { create(:project, name: 'another-project') }
341 342 343 344 345 346 347

      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
348
      let(:another_project) { create(:project, namespace: namespace) }
349

350
      it 'returns path to the project' do
351 352 353
        expect(project.to_reference(another_project)).to eq 'sample-project'
      end
    end
354

355 356
    context 'when different namespace / cross-project argument' do
      let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) }
357
      let(:another_project)   { create(:project, path: 'another-project', namespace: another_namespace) }
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374

      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
375 376
      end
    end
377 378 379 380 381
  end

  describe '#to_human_reference' do
    let(:owner) { create(:user, name: 'Gitlab') }
    let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
382
    let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
383 384 385 386 387 388 389 390 391 392 393 394 395 396

    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
397
      let(:another_namespace_project) { create(:project, name: 'another-project') }
398 399 400 401 402 403 404

      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
405
      let(:another_project) { create(:project, namespace: namespace) }
406 407 408 409

      it 'returns name of the project' do
        expect(project.to_human_reference(another_project)).to eq 'Sample project'
      end
410 411 412
    end
  end

V
Valery Sizov 已提交
413 414 415 416 417 418 419 420 421 422 423 424
  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

425
  describe '#repository_storage_path' do
426
    let(:project) { create(:project) }
427 428

    it 'returns the repository storage path' do
429
      expect(Dir.exist?(project.repository_storage_path)).to be(true)
430 431 432
    end
  end

433
  it 'returns valid url to repo' do
434
    project = described_class.new(path: 'somewhere')
435
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
G
gitlabhq 已提交
436 437
  end

D
Douwe Maan 已提交
438
  describe "#web_url" do
439
    let(:project) { create(:project, path: "somewhere") }
D
Douwe Maan 已提交
440 441

    it 'returns the full web URL for this repo' do
442
      expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
D
Douwe Maan 已提交
443
    end
A
Ariejan de Vroom 已提交
444 445
  end

446
  describe "#new_issue_address" do
447
    let(:project) { create(:project, path: "somewhere") }
448 449
    let(:user) { create(:user) }

450 451 452 453 454 455
    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
456
        address = "p+#{project.full_path}+#{user.incoming_email_token}@gl.ab"
457 458 459 460 461 462 463 464 465

        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
466

467 468 469
      it 'returns nil' do
        expect(project.new_issue_address(user)).to be_nil
      end
470 471 472
    end
  end

473
  describe 'last_activity methods' do
S
Stan Hu 已提交
474 475
    let(:timestamp) { 2.hours.ago }
    # last_activity_at gets set to created_at upon creation
476
    let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
G
gitlabhq 已提交
477

478
    describe 'last_activity' do
479
      it 'alias last_activity to last_event' do
480
        last_event = create(:event, :closed, project: project)
481

482
        expect(project.last_activity).to eq(last_event)
483
      end
G
gitlabhq 已提交
484 485
    end

486 487
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
488
        new_event = create(:event, :closed, project: project, created_at: Time.now)
489

S
Stan Hu 已提交
490
        project.reload
491
        expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
492
      end
493

494
      it 'returns the project\'s last update date if it has no events' do
495
        expect(project.last_activity_date).to eq(project.updated_at)
496
      end
497 498
    end
  end
499

500
  describe '#get_issue' do
501
    let(:project) { create(:project) }
S
Stan Hu 已提交
502
    let!(:issue)  { create(:issue, project: project) }
503 504 505 506 507
    let(:user)    { create(:user) }

    before do
      project.team << [user, :developer]
    end
508 509 510

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

S
Stan Hu 已提交
514 515 516 517
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

518
      it 'returns nil when no issue found' do
519 520 521 522 523 524
        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
525 526 527 528
      end
    end

    context 'with external issues tracker' do
529
      let!(:internal_issue) { create(:issue, project: project) }
530
      before do
531
        allow(project).to receive(:external_issue_tracker).and_return(true)
532 533
      end

534 535 536 537 538 539 540 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
      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
571 572 573 574 575
      end
    end
  end

  describe '#issue_exists?' do
576
    let(:project) { create(:project) }
577 578 579 580 581 582 583 584 585 586 587 588

    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

L
Lin Jen-Shin 已提交
589
  describe '#to_param' do
590 591 592
    context 'with namespace' do
      before do
        @group = create :group, name: 'gitlab'
593
        @project = create(:project, name: 'gitlabhq', namespace: @group)
594 595
      end

V
Vinnie Okada 已提交
596
      it { expect(@project.to_param).to eq('gitlabhq') }
597
    end
598 599 600

    context 'with invalid path' do
      it 'returns previous path to keep project suitable for use in URLs when persisted' do
601
        project = create(:project, path: 'gitlab')
602 603 604 605 606 607 608
        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
609
        project = build(:project, path: 'gitlab')
610 611 612 613 614 615
        project.path = 'foo&bar'

        expect(project).not_to be_valid
        expect(project.to_param).to eq 'foo&bar'
      end
    end
616
  end
D
Dmitriy Zaporozhets 已提交
617

L
Lin Jen-Shin 已提交
618
  describe '#repository' do
619
    let(:project) { create(:project, :repository) }
D
Dmitriy Zaporozhets 已提交
620

621
    it 'returns valid repo' do
622
      expect(project.repository).to be_kind_of(Repository)
D
Dmitriy Zaporozhets 已提交
623 624
    end
  end
625

L
Lin Jen-Shin 已提交
626
  describe '#default_issues_tracker?' do
627
    it "is true if used internal tracker" do
628
      project = build(:project)
629

630
      expect(project.default_issues_tracker?).to be_truthy
631 632
    end

633
    it "is false if used other tracker" do
634 635 636 637
      # NOTE: The current nature of this factory requires persistence
      project = create(:redmine_project)

      expect(project.default_issues_tracker?).to be_falsey
638 639 640
    end
  end

L
Lin Jen-Shin 已提交
641
  describe '#external_issue_tracker' do
642
    let(:project) { create(:project) }
643 644 645
    let(:ext_project) { create(:redmine_project) }

    context 'on existing projects with no value for has_external_issue_tracker' do
646
      before do
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
        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

L
Lin Jen-Shin 已提交
676
  describe '#cache_has_external_issue_tracker' do
677
    let(:project) { create(:project, has_external_issue_tracker: nil) }
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695

    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
T
Toon Claes 已提交
696 697 698 699 700 701 702 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

    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
734 735
  end

736
  describe '#has_wiki?' do
737 738 739
    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) }
740 741 742 743 744 745 746 747

    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

748
  describe '#external_wiki' do
749
    let(:project) { create(:project) }
750

751 752 753 754 755
    context 'with an active external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: true)
        project.external_wiki
      end
756

757 758 759
      it 'sets :has_external_wiki as true' do
        expect(project.has_external_wiki).to be(true)
      end
760

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

764 765 766 767
        project.services.external_wikis.first.destroy

        expect(project.has_external_wiki).to be(false)
      end
768 769
    end

770 771 772 773
    context 'with an inactive external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: false)
      end
774

775 776 777
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end
778 779
    end

780 781 782 783
    context 'with no external wiki' do
      before do
        project.external_wiki
      end
784

785 786 787 788 789 790 791 792 793 794 795
      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
796 797 798
    end
  end

799 800
  describe '#star_count' do
    it 'counts stars from multiple users' do
C
Ciro Santilli 已提交
801 802
      user1 = create :user
      user2 = create :user
803
      project = create(:project, :public)
C
Ciro Santilli 已提交
804 805

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

C
Ciro Santilli 已提交
807
      user1.toggle_star(project)
808 809
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
810
      user2.toggle_star(project)
811 812 813
      project.reload
      expect(project.reload.star_count).to eq(2)

C
Ciro Santilli 已提交
814
      user1.toggle_star(project)
815 816 817
      project.reload
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
818
      user2.toggle_star(project)
819 820 821 822
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

823
    it 'counts stars on the right project' do
824
      user = create :user
825 826
      project1 = create(:project, :public)
      project2 = create(:project, :public)
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853

      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)
C
Ciro Santilli 已提交
854 855
    end
  end
856

L
Lin Jen-Shin 已提交
857
  describe '#avatar_type' do
858
    let(:project) { create(:project) }
859

860
    it 'is true if avatar is image' do
861
      project.update_attribute(:avatar, 'uploads/avatar.png')
862
      expect(project.avatar_type).to be_truthy
863 864
    end

865
    it 'is false if avatar is html page' do
866
      project.update_attribute(:avatar, 'uploads/avatar.html')
867
      expect(project.avatar_type).to eq(['only images allowed'])
868 869
    end
  end
S
sue445 已提交
870

L
Lin Jen-Shin 已提交
871
  describe '#avatar_url' do
S
sue445 已提交
872 873
    subject { project.avatar_url }

874
    let(:project) { create(:project) }
S
sue445 已提交
875

876
    context 'when avatar file is uploaded' do
T
Tim Zallmann 已提交
877
      let(:project) { create(:project, :public, :with_avatar) }
878
      let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" }
879
      let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" }
S
sue445 已提交
880

881 882 883 884 885 886 887 888
      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
S
sue445 已提交
889 890 891 892 893 894 895
    end

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

896
      let(:avatar_path) { "/#{project.full_path}/avatar" }
S
sue445 已提交
897

898
      it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
S
sue445 已提交
899
    end
900 901

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

904
      it { is_expected.to eq nil }
905
    end
S
sue445 已提交
906
  end
907

908
  describe '#pipeline_for' do
909
    let(:project) { create(:project, :repository) }
910
    let!(:pipeline) { create_pipeline }
K
Kamil Trzcinski 已提交
911

912 913
    shared_examples 'giving the correct pipeline' do
      it { is_expected.to eq(pipeline) }
K
Kamil Trzcinski 已提交
914

915 916
      context 'return latest' do
        let!(:pipeline2) { create_pipeline }
K
Kamil Trzcinski 已提交
917

918
        it { is_expected.to eq(pipeline2) }
K
Kamil Trzcinski 已提交
919
      end
920 921 922 923 924 925 926 927 928 929 930 931 932
    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
K
Kamil Trzcinski 已提交
933

934 935 936 937 938
    def create_pipeline
      create(:ci_pipeline,
             project: project,
             ref: 'master',
             sha: project.commit('master').sha)
K
Kamil Trzcinski 已提交
939
    end
940
  end
941

L
Lin Jen-Shin 已提交
942
  describe '#builds_enabled' do
943
    let(:project) { create(:project) }
944

945 946 947
    subject { project.builds_enabled }

    it { expect(project.builds_enabled?).to be_truthy }
948
  end
Y
Yorick Peterse 已提交
949

950
  describe '.with_shared_runners' do
951
    subject { described_class.with_shared_runners }
952 953

    context 'when shared runners are enabled for project' do
954
      let!(:project) { create(:project, shared_runners_enabled: true) }
955 956 957 958 959 960 961

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

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

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

970
  describe '.cached_count', :use_clean_rails_memory_store_caching do
971
    let(:group)     { create(:group, :public) }
972 973
    let!(:project1) { create(:project, :public, group: group) }
    let!(:project2) { create(:project, :public, group: group) }
974 975

    it 'returns total project count' do
976
      expect(described_class).to receive(:count).once.and_call_original
977 978

      3.times do
979
        expect(described_class.cached_count).to eq(2)
980 981 982 983
      end
    end
  end

Y
Yorick Peterse 已提交
984
  describe '.trending' do
F
Felipe Artur 已提交
985
    let(:group)    { create(:group, :public) }
986 987
    let(:project1) { create(:project, :public, group: group) }
    let(:project2) { create(:project, :public, group: group) }
Y
Yorick Peterse 已提交
988 989 990 991 992 993 994 995

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

      create(:note_on_commit, project: project2)

Y
Yorick Peterse 已提交
996
      TrendingProject.refresh!
Y
Yorick Peterse 已提交
997 998
    end

Y
Yorick Peterse 已提交
999
    subject { described_class.trending.to_a }
Y
Yorick Peterse 已提交
1000

Y
Yorick Peterse 已提交
1001 1002
    it 'sorts projects by the amount of notes in descending order' do
      expect(subject).to eq([project1, project2])
Y
Yorick Peterse 已提交
1003
    end
1004 1005 1006 1007 1008 1009 1010 1011

    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
Y
Yorick Peterse 已提交
1012
  end
Y
Yorick Peterse 已提交
1013

T
Toon Claes 已提交
1014 1015 1016 1017
  describe '.starred_by' do
    it 'returns only projects starred by the given user' do
      user1 = create(:user)
      user2 = create(:user)
1018 1019 1020
      project1 = create(:project)
      project2 = create(:project)
      create(:project)
T
Toon Claes 已提交
1021 1022 1023
      user1.toggle_star(project1)
      user2.toggle_star(project2)

1024
      expect(described_class.starred_by(user1)).to contain_exactly(project1)
T
Toon Claes 已提交
1025 1026 1027
    end
  end

Y
Yorick Peterse 已提交
1028
  describe '.visible_to_user' do
1029
    let!(:project) { create(:project, :private) }
Y
Yorick Peterse 已提交
1030 1031 1032 1033 1034 1035
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
1036
        project.add_user(user, Gitlab::Access::MASTER)
Y
Yorick Peterse 已提交
1037 1038 1039 1040 1041 1042 1043 1044 1045
      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
K
Kamil Trzcinski 已提交
1046

1047
  context 'repository storage by default' do
1048
    let(:project) { create(:project) }
1049 1050

    before do
1051
      storages = {
1052
        'default' => { 'path' => 'tmp/tests/repositories' },
1053
        'picked'  => { 'path' => 'tmp/tests/repositories' }
1054
      }
1055 1056 1057
      allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
    end

1058 1059 1060 1061 1062
    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
1063 1064
  end

K
Kamil Trzcinski 已提交
1065
  context 'shared runners by default' do
1066
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
1067 1068 1069 1070

    subject { project.shared_runners_enabled }

    context 'are enabled' do
1071 1072 1073
      before do
        stub_application_setting(shared_runners_enabled: true)
      end
K
Kamil Trzcinski 已提交
1074 1075 1076 1077 1078

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
1079 1080 1081
      before do
        stub_application_setting(shared_runners_enabled: false)
      end
K
Kamil Trzcinski 已提交
1082 1083 1084 1085 1086

      it { is_expected.to be_falsey }
    end
  end

L
Lin Jen-Shin 已提交
1087
  describe '#any_runners' do
1088
    let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
1089 1090
    let(:specific_runner) { create(:ci_runner) }
    let(:shared_runner) { create(:ci_runner, :shared) }
K
Kamil Trzcinski 已提交
1091 1092 1093

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

1095
      it 'has no runners available' do
K
Kamil Trzcinski 已提交
1096 1097
        expect(project.any_runners?).to be_falsey
      end
1098

1099
      it 'has a specific runner' do
1100
        project.runners << specific_runner
K
Kamil Trzcinski 已提交
1101 1102
        expect(project.any_runners?).to be_truthy
      end
1103

1104
      it 'has a shared runner, but they are prohibited to use' do
K
Kamil Trzcinski 已提交
1105 1106 1107
        shared_runner
        expect(project.any_runners?).to be_falsey
      end
1108

K
Kamil Trzcinski 已提交
1109
      it 'checks the presence of specific runner' do
1110
        project.runners << specific_runner
K
Kamil Trzcinski 已提交
1111 1112 1113
        expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
      end
    end
1114

K
Kamil Trzcinski 已提交
1115 1116
    context 'for shared runners enabled' do
      let(:shared_runners_enabled) { true }
1117

1118
      it 'has a shared runner' do
K
Kamil Trzcinski 已提交
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
        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
1129

1130 1131 1132 1133 1134 1135
  describe '#shared_runners' do
    let!(:runner) { create(:ci_runner, :shared) }

    subject { project.shared_runners }

    context 'when shared runners are enabled for project' do
1136
      let!(:project) { create(:project, shared_runners_enabled: true) }
1137 1138 1139 1140 1141 1142 1143

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

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

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

1152
  describe '#visibility_level_allowed?' do
1153
    let(:project) { create(:project, :internal) }
1154 1155 1156 1157 1158 1159 1160 1161

    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
1162 1163
      let(:project)        { create(:project, :internal) }
      let(:forked_project) { create(:project, forked_from_project: project) }
1164 1165 1166 1167 1168

      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
1169
  end
1170

1171
  describe '#pages_deployed?' do
1172
    let(:project) { create :project }
1173 1174 1175 1176

    subject { project.pages_deployed? }

    context 'if public folder does exist' do
1177 1178 1179
      before do
        allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
      end
1180 1181 1182 1183 1184 1185 1186 1187 1188

      it { is_expected.to be_truthy }
    end

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

1189 1190
  describe '#pages_url' do
    let(:group) { create :group, name: group_name }
1191
    let(:project) { create :project, namespace: group, name: project_name }
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
    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

1216
  describe '.search' do
1217
    let(:project) { create(:project, description: 'kitten mittens') }
1218

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

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

1227 1228 1229 1230 1231 1232 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
    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
1266 1267 1268 1269 1270

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

      expect(relation.search(project.namespace.name)).to eq([project])
1271
    end
1272 1273

    describe 'with pending_delete project' do
1274
      let(:pending_delete_project) { create(:project, pending_delete: true) }
1275 1276 1277 1278 1279 1280 1281

      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
1282
  end
1283 1284

  describe '#expire_caches_before_rename' do
1285
    let(:project) { create(:project, :repository) }
1286 1287 1288 1289
    let(:repo)    { double(:repo, exists?: true) }
    let(:wiki)    { double(:wiki, exists?: true) }

    it 'expires the caches of the repository and wiki' do
1290 1291 1292
      allow(Repository).to receive(:new)
        .with('foo', project)
        .and_return(repo)
1293

1294 1295 1296
      allow(Repository).to receive(:new)
        .with('foo.wiki', project)
        .and_return(wiki)
1297

1298 1299
      expect(repo).to receive(:before_delete)
      expect(wiki).to receive(:before_delete)
1300 1301 1302 1303

      project.expire_caches_before_rename('foo')
    end
  end
1304 1305

  describe '.search_by_title' do
1306
    let(:project) { create(:project, name: 'kittens') }
1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319

    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
1320 1321 1322 1323 1324

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

1325 1326
    let(:private_project)  { create :project, :private, group: private_group }
    let(:internal_project) { create :project, :internal, group: internal_group }
1327 1328 1329 1330 1331 1332 1333 1334 1335

    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
1336

1337
  describe '#create_repository' do
1338
    let(:project) { create(:project, :repository) }
1339 1340 1341 1342 1343 1344 1345 1346
    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
1347
        expect(shell).to receive(:add_repository)
J
Jacob Vosmaer 已提交
1348
          .with(project.repository_storage, project.disk_path)
1349
          .and_return(true)
1350 1351 1352 1353 1354 1355 1356

        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
1357
        expect(shell).to receive(:add_repository)
J
Jacob Vosmaer 已提交
1358
          .with(project.repository_storage, project.disk_path)
1359
          .and_return(false)
1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376

        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
1377

1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
  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)
1391
        .with(project.repository_storage_path, project.disk_path)
1392 1393
        .and_return(true)

1394
      expect(project).to receive(:create_repository).with(force: true)
1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406

      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
1407 1408 1409 1410 1411 1412 1413 1414

    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)
J
Jacob Vosmaer 已提交
1415
        .with(project.repository_storage, project.disk_path)
1416 1417 1418 1419
        .and_return(true)

      project.ensure_repository
    end
1420 1421
  end

1422
  describe '#user_can_push_to_empty_repo?' do
1423
    let(:project) { create(:project) }
1424
    let(:user)    { create(:user) }
1425

1426 1427 1428
    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)
1429

1430
      expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
1431 1432
    end

1433 1434 1435
    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)
1436

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

1440 1441 1442
    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)
1443

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

1447 1448 1449
    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)
1450

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

1454 1455
    it 'returns true when user is master' do
      project.team << [user, :master]
1456

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

A
Andre Guedes 已提交
1461
  describe '#container_registry_url' do
1462
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
1463

A
Andre Guedes 已提交
1464
    subject { project.container_registry_url }
K
Kamil Trzcinski 已提交
1465

1466 1467 1468
    before do
      stub_container_registry_config(**registry_settings)
    end
K
Kamil Trzcinski 已提交
1469 1470 1471

    context 'for enabled registry' do
      let(:registry_settings) do
1472 1473
        { enabled: true,
          host_port: 'example.com' }
K
Kamil Trzcinski 已提交
1474 1475
      end

1476
      it { is_expected.not_to be_nil }
K
Kamil Trzcinski 已提交
1477 1478 1479 1480
    end

    context 'for disabled registry' do
      let(:registry_settings) do
1481
        { enabled: false }
K
Kamil Trzcinski 已提交
1482 1483 1484 1485 1486 1487
      end

      it { is_expected.to be_nil }
    end
  end

1488
  describe '#has_container_registry_tags?' do
1489
    let(:project) { create(:project) }
1490 1491

    context 'when container registry is enabled' do
1492 1493 1494
      before do
        stub_container_registry_config(enabled: true)
      end
1495 1496 1497 1498 1499 1500 1501 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

      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
1532 1533 1534
      before do
        stub_container_registry_config(enabled: false)
      end
1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551

      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

1552
  describe '#ci_config_path=' do
1553
    let(:project) { create(:project) }
1554 1555

    it 'sets nil' do
1556
      project.update!(ci_config_path: nil)
1557

1558
      expect(project.ci_config_path).to be_nil
1559 1560 1561
    end

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

1564
      expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
1565 1566
    end

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

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

1574
  describe 'Project import job' do
1575
    let(:project) { create(:project, import_url: generate(:url)) }
1576 1577

    before do
S
Stan Hu 已提交
1578
      allow_any_instance_of(Gitlab::Shell).to receive(:import_repository)
1579
        .with(project.repository_storage_path, project.disk_path, project.import_url)
S
Stan Hu 已提交
1580 1581 1582 1583
        .and_return(true)

      expect_any_instance_of(Repository).to receive(:after_import)
        .and_call_original
1584 1585 1586 1587 1588
    end

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

1589
      expect { project.import_schedule }.to change { project.import_jid }
1590 1591 1592 1593
      expect(project.reload.import_status).to eq('finished')
    end
  end

1594 1595
  describe 'project import state transitions' do
    context 'state transition: [:started] => [:finished]' do
L
Lin Jen-Shin 已提交
1596
      let(:after_import_service) { spy(:after_import_service) }
L
Lin Jen-Shin 已提交
1597
      let(:housekeeping_service) { spy(:housekeeping_service) }
1598 1599

      before do
L
Lin Jen-Shin 已提交
1600 1601
        allow(Projects::AfterImportService)
          .to receive(:new) { after_import_service }
L
Lin Jen-Shin 已提交
1602

L
Lin Jen-Shin 已提交
1603
        allow(after_import_service)
L
Lin Jen-Shin 已提交
1604 1605 1606 1607
          .to receive(:execute) { housekeeping_service.execute }

        allow(Projects::HousekeepingService)
          .to receive(:new) { housekeeping_service }
1608 1609
      end

1610 1611 1612 1613 1614 1615 1616
      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

1617 1618 1619 1620 1621
      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

L
Lin Jen-Shin 已提交
1622
        expect(after_import_service).to have_received(:execute)
1623 1624 1625 1626
        expect(housekeeping_service).to have_received(:execute)
      end

      it 'does not perform housekeeping when project repository does not exist' do
1627
        project = create(:project, :import_started, import_type: :github)
1628 1629 1630 1631 1632 1633 1634

        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
1635
        project = create(:project, :import_started, import_type: nil)
1636 1637 1638 1639 1640 1641 1642 1643

        project.import_finish

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

1644
  describe '#latest_successful_builds_for' do
L
Lin Jen-Shin 已提交
1645
    def create_pipeline(status = 'success')
1646
      create(:ci_pipeline, project: project,
L
Lin Jen-Shin 已提交
1647
                           sha: project.commit.sha,
1648
                           ref: project.default_branch,
L
Lin Jen-Shin 已提交
1649
                           status: status)
1650 1651
    end

L
Lin Jen-Shin 已提交
1652 1653 1654
    def create_build(new_pipeline = pipeline, name = 'test')
      create(:ci_build, :success, :artifacts,
             pipeline: new_pipeline,
L
Lin Jen-Shin 已提交
1655
             status: new_pipeline.status,
L
Lin Jen-Shin 已提交
1656
             name: name)
1657 1658
    end

1659
    let(:project) { create(:project, :repository) }
L
Lin Jen-Shin 已提交
1660
    let(:pipeline) { create_pipeline }
L
Lin Jen-Shin 已提交
1661 1662

    context 'with many builds' do
1663
      it 'gives the latest builds from latest pipeline' do
1664 1665
        pipeline1 = create_pipeline
        pipeline2 = create_pipeline
1666
        build1_p2 = create_build(pipeline2, 'test')
1667 1668
        create_build(pipeline1, 'test')
        create_build(pipeline1, 'test2')
1669
        build2_p2 = create_build(pipeline2, 'test2')
L
Lin Jen-Shin 已提交
1670 1671 1672

        latest_builds = project.latest_successful_builds_for

1673
        expect(latest_builds).to contain_exactly(build2_p2, build1_p2)
L
Lin Jen-Shin 已提交
1674 1675
      end
    end
L
Lin Jen-Shin 已提交
1676

L
Lin Jen-Shin 已提交
1677
    context 'with succeeded pipeline' do
L
Lin Jen-Shin 已提交
1678
      let!(:build) { create_build }
1679

L
Lin Jen-Shin 已提交
1680
      context 'standalone pipeline' do
1681 1682 1683 1684 1685 1686 1687 1688
        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')
1689

1690 1691 1692
          expect(builds).to be_kind_of(ActiveRecord::Relation)
          expect(builds).to be_empty
        end
1693 1694
      end

L
Lin Jen-Shin 已提交
1695
      context 'with some pending pipeline' do
1696
        before do
L
Lin Jen-Shin 已提交
1697
          create_build(create_pipeline('pending'))
1698 1699
        end

L
Lin Jen-Shin 已提交
1700 1701
        it 'gives the latest build from latest pipeline' do
          latest_build = project.latest_successful_builds_for
1702

L
Lin Jen-Shin 已提交
1703
          expect(latest_build).to contain_exactly(build)
1704
        end
1705 1706 1707 1708 1709 1710
      end
    end

    context 'with pending pipeline' do
      before do
        pipeline.update(status: 'pending')
L
Lin Jen-Shin 已提交
1711
        create_build(pipeline)
1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
      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

1723
  describe '#add_import_job' do
1724 1725
    let(:import_jid) { '123' }

1726
    context 'forked' do
1727
      let(:forked_project_link) { create(:forked_project_link, :forked_to_empty_project) }
1728 1729 1730 1731
      let(:forked_from_project) { forked_project_link.forked_from_project }
      let(:project) { forked_project_link.forked_to_project }

      it 'schedules a RepositoryForkWorker job' do
1732 1733 1734 1735 1736
        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)
1737

1738
        expect(project.add_import_job).to eq(import_jid)
1739 1740 1741 1742 1743
      end
    end

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

1746 1747
        expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
        expect(project.add_import_job).to eq(import_jid)
1748 1749 1750 1751
      end
    end
  end

R
Rémy Coutable 已提交
1752
  describe '#gitlab_project_import?' do
1753
    subject(:project) { build(:project, import_type: 'gitlab_project') }
R
Rémy Coutable 已提交
1754 1755 1756 1757 1758

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

  describe '#gitea_import?' do
1759
    subject(:project) { build(:project, import_type: 'gitea') }
R
Rémy Coutable 已提交
1760 1761 1762 1763

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

1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778
  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

1779
  describe '#lfs_enabled?' do
1780
    let(:project) { create(:project) }
1781 1782 1783 1784 1785 1786 1787 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

    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

1841
  describe '#change_head' do
1842
    let(:project) { create(:project, :repository) }
1843

1844 1845 1846 1847 1848
    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

1849
    it 'calls the before_change_head and after_change_head methods' do
1850
      expect(project.repository).to receive(:before_change_head)
1851 1852
      expect(project.repository).to receive(:after_change_head)

1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872
      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
Y
Yorick Peterse 已提交
1873

1874
  context 'forks' do
B
Bob Van Landuyt 已提交
1875 1876
    include ProjectForksHelper

1877 1878 1879 1880 1881
    let(:project) { create(:project, :public) }
    let!(:forked_project) { fork_project(project) }

    describe '#fork_network' do
      it 'includes a fork of the project' do
B
Bob Van Landuyt 已提交
1882
        expect(project.fork_network.projects).to include(forked_project)
1883 1884 1885 1886 1887
      end

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

B
Bob Van Landuyt 已提交
1888
        expect(project.fork_network.projects).to include(other_fork)
1889 1890 1891 1892 1893
      end

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

B
Bob Van Landuyt 已提交
1894
        expect(forked_project.fork_network.projects).to include(other_fork)
1895 1896 1897
      end

      it 'includes the base project' do
B
Bob Van Landuyt 已提交
1898
        expect(forked_project.fork_network.projects).to include(project.reload)
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926
      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
  end

Y
Yorick Peterse 已提交
1927
  describe '#pushes_since_gc' do
1928
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949

    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
1950
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963

    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
1964
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977

    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
1978

1979 1980
  describe '#deployment_variables' do
    context 'when project has no deployment service' do
1981
      let(:project) { create(:project) }
1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998

      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

1999
  describe '#secret_variables_for' do
2000
    let(:project) { create(:project) }
2001 2002 2003 2004 2005 2006 2007 2008 2009

    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

L
Lin Jen-Shin 已提交
2010 2011 2012 2013 2014 2015
    subject { project.secret_variables_for(ref: 'ref') }

    before do
      stub_application_setting(
        default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end
2016 2017 2018

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
2019
        is_expected.to contain_exactly(secret_variable, protected_variable)
2020 2021 2022 2023
      end
    end

    context 'when the ref is not protected' do
2024
      it 'contains only the secret variables' do
2025
        is_expected.to contain_exactly(secret_variable)
2026 2027 2028
      end
    end

2029 2030 2031
    context 'when the ref is a protected branch' do
      before do
        create(:protected_branch, name: 'ref', project: project)
2032
      end
2033 2034 2035 2036 2037 2038 2039 2040 2041 2042

      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'
2043 2044 2045
    end
  end

2046
  describe '#protected_for?' do
2047
    let(:project) { create(:project) }
2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082

    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

M
Markus Koller 已提交
2083
  describe '#update_project_statistics' do
2084
    let(:project) { create(:project) }
M
Markus Koller 已提交
2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102

    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

2103
  describe 'inside_path' do
2104 2105 2106
    let!(:project1) { create(:project, namespace: create(:namespace, path: 'name_pace')) }
    let!(:project2) { create(:project) }
    let!(:project3) { create(:project, namespace: create(:namespace, path: 'namespace')) }
2107
    let!(:path) { project1.namespace.full_path }
2108

2109
    it 'returns correct project' do
2110
      expect(described_class.inside_path(path)).to eq([project1])
2111
    end
2112 2113
  end

D
Douwe Maan 已提交
2114
  describe '#route_map_for' do
2115
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
2116 2117 2118 2119 2120 2121 2122 2123
    let(:route_map) do
      <<-MAP.strip_heredoc
      - source: /source/(.*)/
        public: '\\1'
      MAP
    end

    before do
2124
      project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
D
Douwe Maan 已提交
2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151
    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
2152
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189
    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

2190
  describe '#parent' do
2191
    let(:project) { create(:project) }
2192 2193 2194 2195

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

2196 2197 2198 2199 2200 2201
  describe '#parent_id' do
    let(:project) { create(:project) }

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

2202
  describe '#parent_changed?' do
2203
    let(:project) { create(:project) }
2204

2205 2206 2207
    before do
      project.namespace_id = 7
    end
2208 2209 2210 2211

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

2212 2213 2214
  def enable_lfs
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
  end
K
Kamil Trzcinski 已提交
2215

2216
  describe '#pages_url' do
2217 2218
    let(:group) { create :group, name: 'Group' }
    let(:nested_group) { create :group, parent: group }
K
Kamil Trzcinski 已提交
2219 2220 2221 2222 2223 2224 2225 2226 2227
    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

2228
    context 'top-level group' do
2229
      let(:project) { create :project, namespace: group, name: project_name }
K
Kamil Trzcinski 已提交
2230

2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241
      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
K
Kamil Trzcinski 已提交
2242 2243
    end

2244
    context 'nested group' do
2245
      let(:project) { create :project, namespace: nested_group, name: project_name }
2246
      let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" }
K
Kamil Trzcinski 已提交
2247

2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258
      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
K
Kamil Trzcinski 已提交
2259 2260
    end
  end
2261 2262

  describe '#http_url_to_repo' do
2263
    let(:project) { create :project }
2264

2265 2266 2267
    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('@')
2268 2269
    end
  end
2270 2271

  describe '#pipeline_status' do
2272
    let(:project) { create(:project, :repository) }
2273
    it 'builds a pipeline status' do
2274
      expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
2275 2276 2277 2278 2279 2280
    end

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

  describe '#append_or_update_attribute' do
2283
    let(:project) { create(:project) }
2284 2285 2286 2287 2288

    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'

2289 2290
      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
        .to raise_error(ActiveRecord::RecordNotSaved, error_message)
2291 2292 2293 2294 2295
    end

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

2296 2297
      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
        .not_to raise_error
2298 2299
    end
  end
2300 2301 2302

  describe '#last_repository_updated_at' do
    it 'sets to created_at upon creation' do
2303
      project = create(:project, created_at: 2.hours.ago)
2304 2305 2306 2307

      expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
    end
  end
2308 2309 2310 2311 2312

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

    let!(:private_project) do
2313
      create(:project, :private, creator: user, namespace: user.namespace)
2314 2315
    end

2316
    let!(:public_project) { create(:project, :public) }
2317 2318 2319

    context 'with a user' do
      let(:projects) do
2320
        described_class.all.public_or_visible_to_user(user)
2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333
      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
2334
        projects = described_class.all.public_or_visible_to_user
2335 2336 2337 2338 2339

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

2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362
  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

2363
  describe '#remove_private_deploy_keys' do
2364
    let!(:project) { create(:project) }
2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379

    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
2380
          another_project = create(:project)
2381 2382
          create(:deploy_keys_project, deploy_key: key, project: another_project)
        end
2383

2384 2385
        it 'does not remove the key' do
          project.remove_private_deploy_keys
2386

2387 2388 2389 2390 2391 2392 2393 2394
          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) }
2395

2396 2397
      it 'does not remove the key' do
        project.remove_private_deploy_keys
2398

2399 2400
        expect(project.deploy_keys).to include(key)
      end
2401 2402
    end
  end
2403

2404 2405
  describe '#remove_pages' do
    let(:project) { create(:project) }
2406
    let(:namespace) { project.namespace }
2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417
    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

2418 2419 2420 2421 2422 2423 2424 2425
    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

2426 2427 2428 2429 2430 2431 2432 2433
    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
2434 2435 2436 2437 2438 2439

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

      project.destroy
    end
2440 2441
  end

2442 2443 2444 2445 2446 2447 2448 2449 2450
  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
2451 2452 2453 2454 2455

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

2456 2457 2458 2459
    before do
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
    end

2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471
    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

2472
    describe '#ensure_storage_path_exists' do
2473 2474 2475
      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)

2476
        project.ensure_storage_path_exists
2477 2478 2479
      end
    end

2480 2481
    describe '#legacy_storage?' do
      it 'returns true when storage_version is nil' do
2482
        project = build(:project, storage_version: nil)
2483 2484 2485

        expect(project.legacy_storage?).to be_truthy
      end
2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497

      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
        expect(project.hashed_storage?).to be_falsey
      end
2498 2499
    end

2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 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
    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 }

2547
        it { expect { subject }.to raise_error(StandardError) }
2548 2549
      end
    end
2550 2551 2552 2553 2554 2555

    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
2556 2557 2558 2559 2560 2561

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

T
Toon Claes 已提交
2562
      it 'flags as read-only' do
2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587
        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
2588 2589 2590
  end

  context 'hashed storage' do
2591
    let(:project) { create(:project, :repository) }
2592 2593 2594 2595
    let(:gitlab_shell) { Gitlab::Shell.new }
    let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }

    before do
2596
      stub_application_setting(hashed_storage_enabled: true)
2597
      allow(Digest::SHA2).to receive(:hexdigest) { hash }
2598
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
2599 2600
    end

2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612
    describe '#legacy_storage?' do
      it 'returns false' do
        expect(project.legacy_storage?).to be_falsey
      end
    end

    describe '#hashed_storage?' do
      it 'returns true' do
        expect(project.hashed_storage?).to be_truthy
      end
    end

2613 2614
    describe '#base_dir' do
      it 'returns base_dir based on hash of project id' do
2615
        expect(project.base_dir).to eq('@hashed/6b/86')
2616 2617 2618 2619
      end
    end

    describe '#disk_path' do
2620
      it 'returns disk_path based on hash of project id' do
2621
        hashed_path = '@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b'
2622 2623 2624 2625 2626

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

2627
    describe '#ensure_storage_path_exists' do
2628
      it 'delegates to gitlab_shell to ensure namespace is created' do
2629
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, '@hashed/6b/86')
2630

2631
        project.ensure_storage_path_exists
2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673
      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_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 }

2674
        it { expect { subject }.to raise_error(StandardError) }
2675 2676
      end
    end
2677 2678 2679 2680 2681 2682

    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
2683 2684 2685 2686 2687 2688

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

T
Toon Claes 已提交
2689
      it 'does not flag as read-only' do
2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702
        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
2703
  end
2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 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 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801

  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

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

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 2876
  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
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

  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

S
Stan Hu 已提交
2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922
  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

2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943
  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
G
gitlabhq 已提交
2944
end