project_spec.rb 152 KB
Newer Older
1 2
# frozen_string_literal: true

gitlabhq's avatar
gitlabhq committed
3 4
require 'spec_helper'

5
describe Project do
6
  include ProjectForksHelper
7
  include GitHelpers
8
  include ExternalAuthorizationServiceHelpers
9

Shinya Maeda's avatar
Shinya Maeda committed
10
  it_behaves_like 'having unique enum values'
Shinya Maeda's avatar
Shinya Maeda committed
11

12
  describe 'associations' do
13 14 15
    it { is_expected.to belong_to(:group) }
    it { is_expected.to belong_to(:namespace) }
    it { is_expected.to belong_to(:creator).class_name('User') }
16
    it { is_expected.to belong_to(:pool_repository) }
17
    it { is_expected.to have_many(:users) }
ubudzisz's avatar
ubudzisz committed
18
    it { is_expected.to have_many(:services) }
19 20 21 22 23
    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) }
24
    it { is_expected.to have_many(:users).through(:project_members) }
25 26 27 28
    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) }
29
    it { is_expected.to have_many(:deploy_keys) }
30 31 32 33 34
    it { is_expected.to have_many(:hooks) }
    it { is_expected.to have_many(:protected_branches) }
    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) }
35
    it { is_expected.to have_one(:hangouts_chat_service) }
36
    it { is_expected.to have_one(:packagist_service) }
37 38 39 40
    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) }
blackst0ne's avatar
blackst0ne committed
41
    it { is_expected.to have_one(:discord_service) }
42 43 44 45 46
    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) }
47
    it { is_expected.to have_one(:hipchat_service) }
48 49 50 51 52 53 54 55 56
    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(: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) }
57
    it { is_expected.to have_one(:youtrack_service) }
58 59 60 61 62
    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) }
63
    it { is_expected.to have_one(:project_repository) }
64 65
    it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
    it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
ubudzisz's avatar
ubudzisz committed
66
    it { is_expected.to have_one(:last_event).class_name('Event') }
67
    it { is_expected.to have_one(:forked_from_project).through(:fork_network_member) }
Zeger-Jan van de Weg's avatar
Zeger-Jan van de Weg committed
68
    it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
69
    it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
70
    it { is_expected.to have_many(:commit_statuses) }
71
    it { is_expected.to have_many(:ci_pipelines) }
72
    it { is_expected.to have_many(:builds) }
73
    it { is_expected.to have_many(:build_trace_section_names)}
74 75 76 77
    it { is_expected.to have_many(:runner_projects) }
    it { is_expected.to have_many(:runners) }
    it { is_expected.to have_many(:variables) }
    it { is_expected.to have_many(:triggers) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
78
    it { is_expected.to have_many(:pages_domains) }
79 80
    it { is_expected.to have_many(:labels).class_name('ProjectLabel') }
    it { is_expected.to have_many(:users_star_projects) }
81
    it { is_expected.to have_many(:repository_languages) }
82 83 84 85 86 87 88
    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) }
89 90
    it { is_expected.to have_many(:forked_to_members).class_name('ForkNetworkMember') }
    it { is_expected.to have_many(:forks).through(:forked_to_members) }
Jan Provaznik's avatar
Jan Provaznik committed
91
    it { is_expected.to have_many(:uploads) }
92
    it { is_expected.to have_many(:pipeline_schedules) }
93
    it { is_expected.to have_many(:members_and_requesters) }
94
    it { is_expected.to have_many(:clusters) }
95
    it { is_expected.to have_many(:kubernetes_namespaces) }
96
    it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') }
97
    it { is_expected.to have_many(:project_badges).class_name('ProjectBadge') }
98
    it { is_expected.to have_many(:lfs_file_locks) }
99 100
    it { is_expected.to have_many(:project_deploy_tokens) }
    it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) }
101
    it { is_expected.to have_many(:cycle_analytics_stages) }
102
    it { is_expected.to have_many(:external_pull_requests) }
103

104 105 106 107
    it 'has an inverse relationship with merge requests' do
      expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
    end

108 109 110 111 112 113 114 115 116 117 118 119 120 121
    it 'has a distinct has_many :lfs_objects relation through lfs_objects_projects' do
      project = create(:project)
      lfs_object = create(:lfs_object)
      [:project, :design].each do |repository_type|
        create(:lfs_objects_project, project: project,
                                     lfs_object: lfs_object,
                                     repository_type: repository_type)
      end

      expect(project.lfs_objects_projects.size).to eq(2)
      expect(project.lfs_objects.size).to eq(1)
      expect(project.lfs_objects.to_a).to eql([lfs_object])
    end

122 123
    context 'after initialized' do
      it "has a project_feature" do
124
        expect(described_class.new.project_feature).to be_present
125 126 127
      end
    end

128 129 130 131 132 133 134 135 136
    context 'when creating a new project' do
      it 'automatically creates a CI/CD settings row' do
        project = create(:project)

        expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting)
        expect(project.ci_cd_settings).to be_persisted
      end
    end

137 138 139 140
    context 'updating cd_cd_settings' do
      it 'does not raise an error' do
        project = create(:project)

James Lopez's avatar
James Lopez committed
141
        expect { project.update(ci_cd_settings: nil) }.not_to raise_exception
142 143 144
      end
    end

145
    describe '#members & #requesters' do
146
      let(:project) { create(:project, :public, :access_requestable) }
147 148 149 150
      let(:requester) { create(:user) }
      let(:developer) { create(:user) }
      before do
        project.request_access(requester)
151
        project.add_developer(developer)
152 153
      end

154 155
      it_behaves_like 'members and requesters associations' do
        let(:namespace) { project }
156 157
      end
    end
158

159
    describe 'ci_pipelines association' do
160 161
      it 'returns only pipelines from ci_sources' do
        expect(Ci::Pipeline).to receive(:ci_sources).and_call_original
162

163
        subject.ci_pipelines
164 165
      end
    end
gitlabhq's avatar
gitlabhq committed
166 167
  end

168 169 170 171 172 173 174 175
  describe 'modules' do
    subject { described_class }

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

178
  describe 'validation' do
179
    let!(:project) { create(:project) }
180

181 182
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
183
    it { is_expected.to validate_length_of(:name).is_at_most(255) }
184
    it { is_expected.to validate_presence_of(:path) }
185 186
    it { is_expected.to validate_length_of(:path).is_at_most(255) }
    it { is_expected.to validate_length_of(:description).is_at_most(2000) }
187 188 189
    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) }
190
    it { is_expected.not_to allow_value('/test/foo').for(:ci_config_path) }
191 192
    it { is_expected.to validate_presence_of(:creator) }
    it { is_expected.to validate_presence_of(:namespace) }
193
    it { is_expected.to validate_presence_of(:repository_storage) }
194

195 196 197 198 199
    it 'validates build timeout constraints' do
      is_expected.to validate_numericality_of(:build_timeout)
        .only_integer
        .is_greater_than_or_equal_to(10.minutes)
        .is_less_than(1.month)
200
        .with_message('needs to be between 10 minutes and 1 month')
201 202
    end

203
    it 'does not allow new projects beyond user limits' do
204
      project2 = build(:project)
205 206 207 208 209 210 211

      allow(project2)
        .to receive(:creator)
        .and_return(
          double(can_create_project?: false, projects_limit: 0).as_null_object
        )

212
      expect(project2).not_to be_valid
213
    end
214

215 216 217 218 219 220 221
    it 'validates the visibility' do
      expect_any_instance_of(described_class).to receive(:visibility_level_allowed_as_fork).and_call_original
      expect_any_instance_of(described_class).to receive(:visibility_level_allowed_by_group).and_call_original

      create(:project)
    end

222 223
    describe 'wiki path conflict' do
      context "when the new path has been used by the wiki of other Project" do
224
        it 'has an error on the name attribute' do
225
          new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
226 227 228 229 230 231 232

          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
233
        it 'has an error on the name attribute' do
234 235
          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
236 237 238 239 240 241

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

243
    context 'repository storages inclusion' do
244
      let(:project2) { build(:project, repository_storage: 'missing') }
245 246

      before do
247
        storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } }
248 249 250
        allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
      end

251
      it "does not allow repository storages that don't match a label in the configuration" do
252 253 254 255
        expect(project2).not_to be_valid
        expect(project2.errors[:repository_storage].first).to match(/is not included in the list/)
      end
    end
256

257 258 259
    describe 'import_url' do
      it 'does not allow an invalid URI as import_url' do
        project = build(:project, import_url: 'invalid://')
James Lopez's avatar
James Lopez committed
260

261 262
        expect(project).not_to be_valid
      end
263

264 265 266
      it 'does allow a SSH URI as import_url for persisted projects' do
        project = create(:project)
        project.import_url = 'ssh://test@gitlab.com/project.git'
267

268 269
        expect(project).to be_valid
      end
270

271 272
      it 'does not allow a SSH URI as import_url for new projects' do
        project = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
273

274 275
        expect(project).not_to be_valid
      end
James Lopez's avatar
James Lopez committed
276

277 278
      it 'does allow a valid URI as import_url' do
        project = build(:project, import_url: 'http://gitlab.com/project.git')
James Lopez's avatar
James Lopez committed
279

280 281
        expect(project).to be_valid
      end
282

283 284
      it 'allows an empty URI' do
        project = build(:project, import_url: '')
285

286 287
        expect(project).to be_valid
      end
288

289 290
      it 'does not produce import data on an empty URI' do
        project = build(:project, import_url: '')
291

292 293
        expect(project.import_data).to be_nil
      end
294

295 296
      it 'does not produce import data on an invalid URI' do
        project = build(:project, import_url: 'test://')
297

298 299
        expect(project.import_data).to be_nil
      end
300

301 302
      it "does not allow import_url pointing to localhost" do
        project = build(:project, import_url: 'http://localhost:9000/t.git')
303

304 305 306
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
      end
307

308 309 310 311 312 313 314
      it 'does not allow import_url pointing to the local network' do
        project = build(:project, import_url: 'https://192.168.1.1')

        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Requests to the local network are not allowed')
      end

315 316
      it "does not allow import_url with invalid ports for new projects" do
        project = build(:project, import_url: 'http://github.com:25/t.git')
317

318 319 320
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 80, 443')
      end
321

322 323 324
      it "does not allow import_url with invalid ports for persisted projects" do
        project = create(:project)
        project.import_url = 'http://github.com:25/t.git'
325

326 327 328
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
      end
329

330 331
      it "does not allow import_url with invalid user" do
        project = build(:project, import_url: 'http://$user:password@github.com/t.git')
332

333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Username needs to start with an alphanumeric character')
      end

      include_context 'invalid urls'

      it 'does not allow urls with CR or LF characters' do
        project = build(:project)

        aggregate_failures do
          urls_with_CRLF.each do |url|
            project.import_url = url

            expect(project).not_to be_valid
            expect(project.errors.full_messages.first).to match(/is blocked: URI is invalid/)
          end
        end
      end
351 352
    end

353 354
    describe 'project pending deletion' do
      let!(:project_pending_deletion) do
355
        create(:project,
356 357 358
               pending_delete: true)
      end
      let(:new_project) do
359
        build(:project,
360 361 362 363 364 365 366 367 368 369 370 371
              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
372 373 374

    describe 'path validation' do
      it 'allows paths reserved on the root namespace' do
375
        project = build(:project, path: 'api')
376 377 378 379 380

        expect(project).to be_valid
      end

      it 'rejects paths reserved on another level' do
381
        project = build(:project, path: 'tree')
382 383 384

        expect(project).not_to be_valid
      end
385 386 387

      it 'rejects nested paths' do
        parent = create(:group, :nested, path: 'environments')
388
        project = build(:project, path: 'folders', namespace: parent)
389 390 391

        expect(project).not_to be_valid
      end
392 393 394

      it 'allows a reserved group name' do
        parent = create(:group)
395
        project = build(:project, path: 'avatar', namespace: parent)
396 397 398

        expect(project).to be_valid
      end
399 400 401 402 403 404

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

        expect(project).to be_valid
      end
405
    end
gitlabhq's avatar
gitlabhq committed
406
  end
407

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
  describe '#all_pipelines' do
    let(:project) { create(:project) }

    before do
      create(:ci_pipeline, project: project, ref: 'master', source: :web)
      create(:ci_pipeline, project: project, ref: 'master', source: :external)
    end

    it 'has all pipelines' do
      expect(project.all_pipelines.size).to eq(2)
    end

    context 'when builds are disabled' do
      before do
        project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
      end

425
      it 'returns .external pipelines' do
426 427 428 429 430 431
        expect(project.all_pipelines).to all(have_attributes(source: 'external'))
        expect(project.all_pipelines.size).to eq(1)
      end
    end
  end

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
  describe '#ci_pipelines' do
    let(:project) { create(:project) }

    before do
      create(:ci_pipeline, project: project, ref: 'master', source: :web)
      create(:ci_pipeline, project: project, ref: 'master', source: :external)
    end

    it 'has ci pipelines' do
      expect(project.ci_pipelines.size).to eq(2)
    end

    context 'when builds are disabled' do
      before do
        project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
      end

449
      it 'returns .external pipelines' do
450 451 452 453 454 455
        expect(project.ci_pipelines).to all(have_attributes(source: 'external'))
        expect(project.ci_pipelines.size).to eq(1)
      end
    end
  end

456
  describe 'project token' do
457
    it 'sets an random token if none provided' do
458
      project = FactoryBot.create(:project, runners_token: '')
Kamil Trzcinski's avatar
Kamil Trzcinski committed
459
      expect(project.runners_token).not_to eq('')
460 461
    end

ubudzisz's avatar
ubudzisz committed
462
    it 'does not set an random token if one provided' do
463
      project = FactoryBot.create(:project, runners_token: 'my-token')
Kamil Trzcinski's avatar
Kamil Trzcinski committed
464
      expect(project.runners_token).to eq('my-token')
465 466
    end
  end
gitlabhq's avatar
gitlabhq committed
467

468
  describe 'Respond to' do
469 470 471 472 473
    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) }
474
    it { is_expected.to respond_to(:full_path) }
gitlabhq's avatar
gitlabhq committed
475 476
  end

477
  describe 'delegation' do
478
    [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_user, :add_users].each do |method|
479 480 481 482 483
      it { is_expected.to delegate_method(method).to(:team) }
    end

    it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) }
    it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
484
    it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
485
    it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
486 487
  end

488 489 490 491 492 493 494 495 496
  describe '#to_reference_with_postfix' do
    it 'returns the full path with reference_postfix' do
      namespace = create(:namespace, path: 'sample-namespace')
      project = create(:project, path: 'sample-project', namespace: namespace)

      expect(project.to_reference_with_postfix).to eq 'sample-namespace/sample-project>'
    end
  end

497
  describe '#to_reference' do
498
    let(:owner)     { create(:user, name: 'Gitlab') }
499
    let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
500
    let(:project)   { create(:project, path: 'sample-project', namespace: namespace) }
501
    let(:group)     { create(:group, name: 'Group', path: 'sample-group') }
502

503
    context 'when nil argument' do
504 505 506 507 508
      it 'returns nil' do
        expect(project.to_reference).to be_nil
      end
    end

509
    context 'when full is true' do
510
      it 'returns complete path to the project' do
511 512 513
        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'
514 515 516 517 518 519 520 521 522 523
      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
524
      let(:another_namespace_project) { create(:project, name: 'another-project') }
525 526 527 528 529 530 531

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

534
      it 'returns path to the project' do
535 536 537
        expect(project.to_reference(another_project)).to eq 'sample-project'
      end
    end
538

539 540
    context 'when different namespace / cross-project argument' do
      let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) }
541
      let(:another_project)   { create(:project, path: 'another-project', namespace: another_namespace) }
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

      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
559 560
      end
    end
561 562 563 564 565
  end

  describe '#to_human_reference' do
    let(:owner) { create(:user, name: 'Gitlab') }
    let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
566
    let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
567 568 569 570 571 572 573 574 575 576 577 578 579 580

    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
581
      let(:another_namespace_project) { create(:project, name: 'another-project') }
582 583 584 585 586 587 588

      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
589
      let(:another_project) { create(:project, namespace: namespace) }
590 591 592 593

      it 'returns name of the project' do
        expect(project.to_human_reference(another_project)).to eq 'Sample project'
      end
594 595 596
    end
  end

597
  describe '#merge_method' do
598 599 600 601 602 603 604
    using RSpec::Parameterized::TableSyntax

    where(:ff, :rebase, :method) do
      true  | true  | :ff
      true  | false | :ff
      false | true  | :rebase_merge
      false | false | :merge
605 606
    end

607 608 609 610 611 612
    with_them do
      let(:project) { build(:project, merge_requests_rebase_enabled: rebase, merge_requests_ff_only_enabled: ff) }

      subject { project.merge_method }

      it { is_expected.to eq(method) }
613 614 615
    end
  end

616
  it 'returns valid url to repo' do
617
    project = described_class.new(path: 'somewhere')
618
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
gitlabhq's avatar
gitlabhq committed
619 620
  end

Douwe Maan's avatar
Douwe Maan committed
621
  describe "#web_url" do
622
    let(:project) { create(:project, path: "somewhere") }
Douwe Maan's avatar
Douwe Maan committed
623 624

    it 'returns the full web URL for this repo' do
625
      expect(project.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere")
Douwe Maan's avatar
Douwe Maan committed
626
    end
627 628
  end

629 630
  describe "#readme_url" do
    context 'with a non-existing repository' do
631
      let(:project) { create(:project) }
632

633
      it 'returns nil' do
634 635 636 637 638 639
        expect(project.readme_url).to be_nil
      end
    end

    context 'with an existing repository' do
      context 'when no README exists' do
640
        let(:project) { create(:project, :empty_repo) }
641

642
        it 'returns nil' do
643 644 645 646 647
          expect(project.readme_url).to be_nil
        end
      end

      context 'when a README exists' do
648 649
        let(:project) { create(:project, :repository) }

650
        it 'returns the README' do
651
          expect(project.readme_url).to eq("#{project.web_url}/blob/master/README.md")
652 653 654 655 656
        end
      end
    end
  end

657
  describe "#new_issuable_address" do
658
    let(:project) { create(:project, path: "somewhere") }
659 660
    let(:user) { create(:user) }

661 662 663 664 665 666
    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
667
        address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-issue@gl.ab"
668

669 670 671 672
        expect(project.new_issuable_address(user, 'issue')).to eq(address)
      end

      it 'returns the address to create a new merge request' do
673
        address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-merge-request@gl.ab"
674 675

        expect(project.new_issuable_address(user, 'merge_request')).to eq(address)
676
      end
677 678 679 680

      it 'returns nil with invalid address type' do
        expect(project.new_issuable_address(user, 'invalid_param')).to be_nil
      end
681 682 683 684 685 686
    end

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

688
      it 'returns nil' do
689 690 691 692 693
        expect(project.new_issuable_address(user, 'issue')).to be_nil
      end

      it 'returns nil' do
        expect(project.new_issuable_address(user, 'merge_request')).to be_nil
694
      end
695 696 697
    end
  end

698
  describe 'last_activity methods' do
699 700
    let(:timestamp) { 2.hours.ago }
    # last_activity_at gets set to created_at upon creation
701
    let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
gitlabhq's avatar
gitlabhq committed
702

703
    describe 'last_activity' do
704
      it 'alias last_activity to last_event' do
705
        last_event = create(:event, :closed, project: project)
706

707
        expect(project.last_activity).to eq(last_event)
708
      end
gitlabhq's avatar
gitlabhq committed
709 710
    end

711 712
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
713
        new_event = create(:event, :closed, project: project, created_at: Time.now)
714

715
        project.reload
716
        expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
717
      end
718

719
      it 'returns the project\'s last update date if it has no events' do
720
        expect(project.last_activity_date).to eq(project.updated_at)
721
      end
722 723

      it 'returns the most recent timestamp' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
724 725 726
        project.update(updated_at: nil,
                       last_activity_at: timestamp,
                       last_repository_updated_at: timestamp - 1.hour)
727

728
        expect(project.last_activity_date).to be_like_time(timestamp)
729

Lin Jen-Shin's avatar
Lin Jen-Shin committed
730 731 732
        project.update(updated_at: timestamp,
                       last_activity_at: timestamp - 1.hour,
                       last_repository_updated_at: nil)
733

734
        expect(project.last_activity_date).to be_like_time(timestamp)
735
      end
736 737
    end
  end
738

739
  describe '#get_issue' do
740
    let(:project) { create(:project) }
741
    let!(:issue)  { create(:issue, project: project) }
742 743 744
    let(:user)    { create(:user) }

    before do
745
      project.add_developer(user)
746
    end
747 748 749

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

753 754 755 756
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

757
      it 'returns nil when no issue found' do
758 759 760 761 762 763
        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
764 765 766 767
      end
    end

    context 'with external issues tracker' do
768
      let!(:internal_issue) { create(:issue, project: project) }
769
      before do
770
        allow(project).to receive(:external_issue_tracker).and_return(true)
771 772
      end

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809
      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
810 811 812 813 814
      end
    end
  end

  describe '#issue_exists?' do
815
    let(:project) { create(:project) }
816 817 818 819 820 821 822 823 824 825 826 827

    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

828
  describe '#to_param' do
829 830
    context 'with namespace' do
      before do
831
        @group = create(:group, name: 'gitlab')
832
        @project = create(:project, name: 'gitlabhq', namespace: @group)
833 834
      end

Vinnie Okada's avatar
Vinnie Okada committed
835
      it { expect(@project.to_param).to eq('gitlabhq') }
836
    end
837 838 839

    context 'with invalid path' do
      it 'returns previous path to keep project suitable for use in URLs when persisted' do
840
        project = create(:project, path: 'gitlab')
841 842 843 844 845 846 847
        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
848
        project = build(:project, path: 'gitlab')
849 850 851 852 853 854
        project.path = 'foo&bar'

        expect(project).not_to be_valid
        expect(project.to_param).to eq 'foo&bar'
      end
    end
855
  end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
856

857
  describe '#repository' do
858
    let(:project) { create(:project, :repository) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
859

860
    it 'returns valid repo' do
861
      expect(project.repository).to be_kind_of(Repository)
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
862 863
    end
  end
864

865
  describe '#default_issues_tracker?' do
866
    it "is true if used internal tracker" do
867
      project = build(:project)
868

869
      expect(project.default_issues_tracker?).to be_truthy
870 871
    end

872
    it "is false if used other tracker" do
873 874 875 876
      # NOTE: The current nature of this factory requires persistence
      project = create(:redmine_project)

      expect(project.default_issues_tracker?).to be_falsey
877 878 879
    end
  end

880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
  describe '#empty_repo?' do
    context 'when the repo does not exist' do
      let(:project) { build_stubbed(:project) }

      it 'returns true' do
        expect(project.empty_repo?).to be(true)
      end
    end

    context 'when the repo exists' do
      let(:project) { create(:project, :repository) }
      let(:empty_project) { create(:project, :empty_repo) }

      it { expect(empty_project.empty_repo?).to be(true) }
      it { expect(project.empty_repo?).to be(false) }
    end
  end

898
  describe '#external_issue_tracker' do
899
    let(:project) { create(:project) }
900 901 902
    let(:ext_project) { create(:redmine_project) }

    context 'on existing projects with no value for has_external_issue_tracker' do
903
      before do
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
        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

933
  describe '#cache_has_external_issue_tracker' do
934
    let(:project) { create(:project, has_external_issue_tracker: nil) }
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952

    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
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990

    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
991 992
  end

993
  describe '#has_wiki?' do
994 995 996
    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) }
997 998 999 1000 1001 1002 1003 1004

    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

1005
  describe '#external_wiki' do
1006
    let(:project) { create(:project) }
1007

1008 1009 1010 1011 1012
    context 'with an active external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: true)
        project.external_wiki
      end
1013

1014 1015 1016
      it 'sets :has_external_wiki as true' do
        expect(project.has_external_wiki).to be(true)
      end
1017

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

1021 1022 1023 1024
        project.services.external_wikis.first.destroy

        expect(project.has_external_wiki).to be(false)
      end
1025 1026
    end

1027 1028 1029 1030
    context 'with an inactive external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: false)
      end
1031

1032 1033 1034
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end
1035 1036
    end

1037 1038 1039 1040
    context 'with no external wiki' do
      before do
        project.external_wiki
      end
1041

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
      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
1053 1054 1055
    end
  end

1056 1057
  describe '#star_count' do
    it 'counts stars from multiple users' do
1058 1059
      user1 = create(:user)
      user2 = create(:user)
1060
      project = create(:project, :public)
Ciro Santilli's avatar
Ciro Santilli committed
1061 1062

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

Ciro Santilli's avatar
Ciro Santilli committed
1064
      user1.toggle_star(project)
1065 1066
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
1067
      user2.toggle_star(project)
1068 1069 1070
      project.reload
      expect(project.reload.star_count).to eq(2)

Ciro Santilli's avatar
Ciro Santilli committed
1071
      user1.toggle_star(project)
1072 1073 1074
      project.reload
      expect(project.reload.star_count).to eq(1)

Ciro Santilli's avatar
Ciro Santilli committed
1075
      user2.toggle_star(project)
1076 1077 1078 1079
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

1080
    it 'counts stars on the right project' do
1081
      user = create(:user)
1082 1083
      project1 = create(:project, :public)
      project2 = create(:project, :public)
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110

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

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

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

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

      user.toggle_star(project2)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(0)
Ciro Santilli's avatar
Ciro Santilli committed
1111 1112
    end
  end
1113

1114
  describe '#avatar_type' do
1115
    let(:project) { create(:project) }
1116

1117
    it 'is true if avatar is image' do
1118
      project.update_attribute(:avatar, 'uploads/avatar.png')
1119
      expect(project.avatar_type).to be_truthy
1120 1121
    end

1122
    it 'is false if avatar is html page' do
1123
      project.update_attribute(:avatar, 'uploads/avatar.html')
1124
      expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico'])
1125 1126
    end
  end
sue445's avatar
sue445 committed
1127

1128
  describe '#avatar_url' do
sue445's avatar
sue445 committed
1129 1130
    subject { project.avatar_url }

1131
    let(:project) { create(:project) }
sue445's avatar
sue445 committed
1132

1133
    context 'when avatar file is uploaded' do
1134
      let(:project) { create(:project, :public, :with_avatar) }
sue445's avatar
sue445 committed
1135

1136
      it 'shows correct url' do
1137 1138
        expect(project.avatar_url).to eq(project.avatar.url)
        expect(project.avatar_url(only_path: false)).to eq([Gitlab.config.gitlab.url, project.avatar.url].join)
1139
      end
sue445's avatar
sue445 committed
1140 1141
    end

1142
    context 'when avatar file in git' do
sue445's avatar
sue445 committed
1143 1144 1145 1146
      before do
        allow(project).to receive(:avatar_in_git) { true }
      end

1147
      let(:avatar_path) { "/#{project.full_path}/-/avatar" }
sue445's avatar
sue445 committed
1148

1149
      it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
sue445's avatar
sue445 committed
1150
    end
1151 1152

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

1155
      it { is_expected.to eq nil }
1156
    end
sue445's avatar
sue445 committed
1157
  end
1158

1159
  describe '#pipeline_for' do
1160
    let(:project) { create(:project, :repository) }
1161

1162 1163
    shared_examples 'giving the correct pipeline' do
      it { is_expected.to eq(pipeline) }
1164

1165
      context 'return latest' do
1166
        let!(:pipeline2) { create_pipeline(project) }
1167

1168
        it { is_expected.to eq(pipeline2) }
1169
      end
1170 1171
    end

Matija Čupić's avatar
Matija Čupić committed
1172 1173 1174 1175 1176
    context 'with a matching pipeline' do
      let!(:pipeline) { create_pipeline(project) }

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

Matija Čupić's avatar
Matija Čupić committed
1178
        it_behaves_like 'giving the correct pipeline'
1179

Matija Čupić's avatar
Matija Čupić committed
1180 1181
        context 'with supplied id' do
          let!(:other_pipeline) { create_pipeline(project) }
1182

Matija Čupić's avatar
Matija Čupić committed
1183
          subject { project.pipeline_for('master', pipeline.sha, other_pipeline.id) }
1184

Matija Čupić's avatar
Matija Čupić committed
1185 1186 1187 1188 1189 1190 1191 1192
          it { is_expected.to eq(other_pipeline) }
        end
      end

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

        it_behaves_like 'giving the correct pipeline'
1193
      end
1194 1195
    end

Matija Čupić's avatar
Matija Čupić committed
1196
    context 'when there is no matching pipeline' do
1197 1198
      subject { project.pipeline_for('master') }

Matija Čupić's avatar
Matija Čupić committed
1199
      it { is_expected.to be_nil }
1200
    end
1201
  end
1202

1203 1204 1205 1206 1207
  describe '#pipelines_for' do
    let(:project) { create(:project, :repository) }
    let!(:pipeline) { create_pipeline(project) }
    let!(:other_pipeline) { create_pipeline(project) }

Matija Čupić's avatar
Matija Čupić committed
1208
    subject { project.pipelines_for(project.default_branch, project.commit.sha) }
1209

Matija Čupić's avatar
Matija Čupić committed
1210
    it { is_expected.to contain_exactly(pipeline, other_pipeline) }
1211 1212
  end

1213
  describe '#builds_enabled' do
1214
    let(:project) { create(:project) }
1215

1216 1217 1218
    subject { project.builds_enabled }

    it { expect(project.builds_enabled?).to be_truthy }
1219
  end
1220

1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
  describe '.sort_by_attribute' do
    it 'reorders the input relation by start count desc' do
      project1 = create(:project, star_count: 2)
      project2 = create(:project, star_count: 1)
      project3 = create(:project)

      projects = described_class.sort_by_attribute(:stars_desc)

      expect(projects).to eq([project1, project2, project3])
    end
  end

1233
  describe '.with_shared_runners' do
1234
    subject { described_class.with_shared_runners }
1235 1236

    context 'when shared runners are enabled for project' do
1237
      let!(:project) { create(:project, shared_runners_enabled: true) }
1238 1239 1240 1241 1242 1243 1244

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

    context 'when shared runners are disabled for project' do
1245
      let!(:project) { create(:project, shared_runners_enabled: false) }
1246 1247 1248 1249 1250 1251 1252

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

1253
  describe '.cached_count', :use_clean_rails_memory_store_caching do
1254
    let(:group)     { create(:group, :public) }
1255 1256
    let!(:project1) { create(:project, :public, group: group) }
    let!(:project2) { create(:project, :public, group: group) }
1257 1258

    it 'returns total project count' do
1259
      expect(described_class).to receive(:count).once.and_call_original
1260 1261

      3.times do
1262
        expect(described_class.cached_count).to eq(2)
1263 1264 1265 1266
      end
    end
  end

1267
  describe '.trending' do
Felipe Artur's avatar
Felipe Artur committed
1268
    let(:group)    { create(:group, :public) }
1269 1270
    let(:project1) { create(:project, :public, group: group) }
    let(:project2) { create(:project, :public, group: group) }
1271 1272 1273 1274 1275 1276 1277 1278

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

      create(:note_on_commit, project: project2)

1279
      TrendingProject.refresh!
1280 1281
    end

1282
    subject { described_class.trending.to_a }
1283

1284 1285
    it 'sorts projects by the amount of notes in descending order' do
      expect(subject).to eq([project1, project2])
1286
    end
1287 1288 1289 1290 1291 1292 1293 1294

    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
1295
  end
1296

1297 1298 1299 1300
  describe '.starred_by' do
    it 'returns only projects starred by the given user' do
      user1 = create(:user)
      user2 = create(:user)
1301 1302 1303
      project1 = create(:project)
      project2 = create(:project)
      create(:project)
1304 1305 1306
      user1.toggle_star(project1)
      user2.toggle_star(project2)

1307
      expect(described_class.starred_by(user1)).to contain_exactly(project1)
1308 1309 1310
    end
  end

1311
  describe '.visible_to_user' do
1312
    let!(:project) { create(:project, :private) }
1313 1314 1315 1316 1317 1318
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
1319
        project.add_user(user, Gitlab::Access::MAINTAINER)
1320 1321 1322 1323 1324 1325 1326 1327 1328
      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
1329

1330
  context 'repository storage by default' do
1331
    let(:project) { build(:project) }
1332 1333

    before do
1334
      storages = {
1335 1336
        'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'),
        'picked'  => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories')
1337
      }
1338 1339 1340
      allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
    end

1341 1342 1343 1344 1345
    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
1346 1347
  end

1348
  context 'shared runners by default' do
1349
    let(:project) { create(:project) }
1350 1351 1352 1353

    subject { project.shared_runners_enabled }

    context 'are enabled' do
1354 1355 1356
      before do
        stub_application_setting(shared_runners_enabled: true)
      end
1357 1358 1359 1360 1361

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
1362 1363 1364
      before do
        stub_application_setting(shared_runners_enabled: false)
      end
1365 1366 1367 1368 1369

      it { is_expected.to be_falsey }
    end
  end

1370
  describe '#any_runners?' do
1371
    context 'shared runners' do
1372
      let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
1373 1374
      let(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
      let(:shared_runner) { create(:ci_runner, :instance) }
1375

1376 1377
      context 'for shared runners disabled' do
        let(:shared_runners_enabled) { false }
1378

1379 1380 1381
        it 'has no runners available' do
          expect(project.any_runners?).to be_falsey
        end
1382

1383
        it 'has a specific runner' do
1384
          specific_runner
1385

1386 1387 1388 1389 1390
          expect(project.any_runners?).to be_truthy
        end

        it 'has a shared runner, but they are prohibited to use' do
          shared_runner
1391

1392 1393
          expect(project.any_runners?).to be_falsey
        end
1394

1395
        it 'checks the presence of specific runner' do
1396
          specific_runner
1397

1398 1399
          expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
        end
1400 1401

        it 'returns false if match cannot be found' do
1402
          specific_runner
1403

1404 1405
          expect(project.any_runners? { false }).to be_falsey
        end
1406
      end
1407

1408 1409 1410 1411 1412
      context 'for shared runners enabled' do
        let(:shared_runners_enabled) { true }

        it 'has a shared runner' do
          shared_runner
1413

1414 1415 1416 1417 1418
          expect(project.any_runners?).to be_truthy
        end

        it 'checks the presence of shared runner' do
          shared_runner
1419

1420 1421
          expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
        end
1422 1423 1424

        it 'returns false if match cannot be found' do
          shared_runner
1425

1426 1427
          expect(project.any_runners? { false }).to be_falsey
        end
1428 1429
      end
    end
1430

1431
    context 'group runners' do
1432 1433
      let(:project) { create(:project, group_runners_enabled: group_runners_enabled) }
      let(:group) { create(:group, projects: [project]) }
1434
      let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
1435 1436 1437

      context 'for group runners disabled' do
        let(:group_runners_enabled) { false }
1438

1439 1440 1441 1442 1443 1444
        it 'has no runners available' do
          expect(project.any_runners?).to be_falsey
        end

        it 'has a group runner, but they are prohibited to use' do
          group_runner
1445

1446 1447
          expect(project.any_runners?).to be_falsey
        end
1448 1449
      end

1450 1451 1452 1453 1454
      context 'for group runners enabled' do
        let(:group_runners_enabled) { true }

        it 'has a group runner' do
          group_runner
1455

1456 1457 1458 1459 1460
          expect(project.any_runners?).to be_truthy
        end

        it 'checks the presence of group runner' do
          group_runner
1461

1462 1463
          expect(project.any_runners? { |runner| runner == group_runner }).to be_truthy
        end
1464 1465 1466

        it 'returns false if match cannot be found' do
          group_runner
1467

1468 1469
          expect(project.any_runners? { false }).to be_falsey
        end
1470 1471 1472
      end
    end
  end
1473

1474
  describe '#shared_runners' do
1475
    let!(:runner) { create(:ci_runner, :instance) }
1476 1477 1478 1479

    subject { project.shared_runners }

    context 'when shared runners are enabled for project' do
1480
      let!(:project) { create(:project, shared_runners_enabled: true) }
1481 1482 1483 1484 1485 1486 1487

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

    context 'when shared runners are disabled for project' do
1488
      let!(:project) { create(:project, shared_runners_enabled: false) }
1489 1490 1491 1492 1493 1494 1495

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

1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
  describe '#visibility_level' do
    let(:project) { build(:project) }

    subject { project.visibility_level }

    context 'by default' do
      it { is_expected.to eq(Gitlab::VisibilityLevel::PRIVATE) }
    end

    context 'when set to INTERNAL in application settings' do
1506 1507
      using RSpec::Parameterized::TableSyntax

1508 1509 1510 1511 1512
      before do
        stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
      end

      it { is_expected.to eq(Gitlab::VisibilityLevel::INTERNAL) }
1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527

      where(:attribute_name, :value) do
        :visibility | 'public'
        :visibility_level | Gitlab::VisibilityLevel::PUBLIC
        'visibility' | 'public'
        'visibility_level' | Gitlab::VisibilityLevel::PUBLIC
      end

      with_them do
        it 'sets the visibility level' do
          proj = described_class.new(attribute_name => value, name: 'test', path: 'test')

          expect(proj.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
        end
      end
1528 1529 1530
    end
  end

1531
  describe '#visibility_level_allowed?' do
1532
    let(:project) { create(:project, :internal) }
1533 1534 1535 1536 1537 1538 1539 1540

    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
1541
      let(:project)        { create(:project, :internal) }
1542
      let(:forked_project) { fork_project(project) }
1543 1544 1545 1546 1547

      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
1548
  end
1549

1550
  describe '#pages_deployed?' do
1551
    let(:project) { create(:project) }
1552 1553 1554 1555

    subject { project.pages_deployed? }

    context 'if public folder does exist' do
1556 1557 1558
      before do
        allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
      end
1559 1560 1561 1562 1563 1564 1565 1566 1567

      it { is_expected.to be_truthy }
    end

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

1568
  describe '#pages_url' do
1569 1570
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
    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") }
1592 1593 1594 1595
    end
  end

  describe '#pages_group_url' do
1596 1597
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619
    let(:domain) { 'Example.com' }
    let(:port) { 1234 }

    subject { project.pages_group_url }

    before do
      allow(Settings.pages).to receive(:host).and_return(domain)
      allow(Gitlab.config.pages).to receive(:url).and_return("http://example.com:#{port}")
    end

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

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

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

      it { is_expected.to eq("http://group.example.com:#{port}") }
1620 1621 1622
    end
  end

1623
  describe '.search' do
1624
    let(:project) { create(:project, description: 'kitten mittens') }
1625

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

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

1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
    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

1662
    describe 'with pending_delete project' do
1663
      let(:pending_delete_project) { create(:project, pending_delete: true) }
1664 1665 1666 1667 1668 1669 1670

      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
1671
  end
1672

1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699
  describe '.optionally_search' do
    let(:project) { create(:project) }

    it 'searches for projects matching the query if one is given' do
      relation = described_class.optionally_search(project.name)

      expect(relation).to eq([project])
    end

    it 'returns the current relation if no search query is given' do
      relation = described_class.where(id: project.id)

      expect(relation.optionally_search).to eq(relation)
    end
  end

  describe '.including_namespace_and_owner' do
    it 'eager loads the namespace and namespace owner' do
      create(:project)

      row = described_class.eager_load_namespace_and_owner.to_a.first
      recorder = ActiveRecord::QueryRecorder.new { row.namespace.owner }

      expect(recorder.count).to be_zero
    end
  end

1700
  describe '#expire_caches_before_rename' do
1701
    let(:project) { create(:project, :repository) }
1702 1703 1704 1705
    let(:repo)    { double(:repo, exists?: true) }
    let(:wiki)    { double(:wiki, exists?: true) }

    it 'expires the caches of the repository and wiki' do
1706 1707 1708
      allow(Repository).to receive(:new)
        .with('foo', project)
        .and_return(repo)
1709

1710 1711 1712
      allow(Repository).to receive(:new)
        .with('foo.wiki', project)
        .and_return(wiki)
1713

1714 1715
      expect(repo).to receive(:before_delete)
      expect(wiki).to receive(:before_delete)
1716 1717 1718 1719

      project.expire_caches_before_rename('foo')
    end
  end
1720 1721

  describe '.search_by_title' do
1722
    let(:project) { create(:project, name: 'kittens') }
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735

    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
1736 1737 1738 1739 1740

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

1741 1742
    let(:private_project)  { create(:project, :private, group: private_group) }
    let(:internal_project) { create(:project, :internal, group: internal_group) }
1743 1744 1745 1746 1747 1748 1749 1750 1751

    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
1752

1753
  describe '#track_project_repository' do
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768
    shared_examples 'tracks storage location' do
      context 'when a project repository entry does not exist' do
        it 'creates a new entry' do
          expect { project.track_project_repository }.to change(project, :project_repository)
        end

        it 'tracks the project storage location' do
          project.track_project_repository

          expect(project.project_repository).to have_attributes(
            disk_path: project.disk_path,
            shard_name: project.repository_storage
          )
        end
      end
1769

1770 1771 1772 1773 1774 1775 1776
      context 'when a tracking entry exists' do
        let!(:project_repository) { create(:project_repository, project: project) }
        let!(:shard) { create(:shard, name: 'foo') }

        it 'does not create a new entry in the database' do
          expect { project.track_project_repository }.not_to change(project, :project_repository)
        end
1777

1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
        it 'updates the project storage location' do
          allow(project).to receive(:disk_path).and_return('fancy/new/path')
          allow(project).to receive(:repository_storage).and_return('foo')

          project.track_project_repository

          expect(project.project_repository).to have_attributes(
            disk_path: 'fancy/new/path',
            shard_name: 'foo'
          )
        end
      end
1790 1791
    end

1792 1793
    context 'with projects on legacy storage' do
      let(:project) { create(:project, :repository, :legacy_storage) }
1794

1795 1796
      it_behaves_like 'tracks storage location'
    end
1797

1798 1799
    context 'with projects on hashed storage' do
      let(:project) { create(:project, :repository) }
1800

1801
      it_behaves_like 'tracks storage location'
1802 1803 1804
    end
  end

1805
  describe '#create_repository' do
1806
    let(:project) { create(:project, :repository) }
1807 1808 1809 1810 1811 1812 1813 1814
    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
1815
        expect(shell).to receive(:create_repository)
1816
          .with(project.repository_storage, project.disk_path, project.full_path)
1817
          .and_return(true)
1818 1819 1820 1821 1822 1823 1824

        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
1825
        expect(shell).to receive(:create_repository)
1826
          .with(project.repository_storage, project.disk_path, project.full_path)
1827
          .and_return(false)
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838

        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)
1839
        expect(shell).not_to receive(:create_repository)
1840 1841 1842 1843 1844

        project.create_repository
      end
    end
  end
1845

1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857
  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)

1858
      allow(shell).to receive(:create_repository)
1859
        .with(project.repository_storage, project.disk_path, project.full_path)
1860 1861
        .and_return(true)

1862
      expect(project).to receive(:create_repository).with(force: true)
1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874

      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
1875 1876 1877 1878 1879 1880 1881

    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)

1882
      expect(shell).to receive(:create_repository)
1883
        .with(project.repository_storage, project.disk_path, project.full_path)
1884 1885 1886 1887
        .and_return(true)

      project.ensure_repository
    end
1888 1889
  end

1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
  describe 'handling import URL' do
    it 'returns the sanitized URL' do
      project = create(:project, :import_started, import_url: 'http://user:pass@test.com')

      project.import_state.finish

      expect(project.reload.import_url).to eq('http://test.com')
    end
  end

1900
  describe '#container_registry_url' do
1901
    let(:project) { create(:project) }
1902

1903
    subject { project.container_registry_url }
1904

1905 1906 1907
    before do
      stub_container_registry_config(**registry_settings)
    end
1908 1909 1910

    context 'for enabled registry' do
      let(:registry_settings) do
1911 1912
        { enabled: true,
          host_port: 'example.com' }
1913 1914
      end

1915
      it { is_expected.not_to be_nil }
1916 1917 1918 1919
    end

    context 'for disabled registry' do
      let(:registry_settings) do
1920
        { enabled: false }
1921 1922 1923 1924 1925 1926
      end

      it { is_expected.to be_nil }
    end
  end

1927
  describe '#has_container_registry_tags?' do
1928
    let(:project) { create(:project) }
1929 1930

    context 'when container registry is enabled' do
1931 1932 1933
      before do
        stub_container_registry_config(enabled: true)
      end
1934 1935 1936 1937 1938 1939 1940 1941 1942

      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

1943
        it 'has image tags' do
1944 1945 1946 1947 1948 1949 1950 1951 1952 1953
          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

1954
        it 'has image tags' do
1955 1956 1957 1958 1959 1960 1961 1962 1963
          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

1964
        it 'does not have image tags' do
1965 1966 1967 1968 1969 1970
          expect(project).not_to have_container_registry_tags
        end
      end
    end

    context 'when container registry is disabled' do
1971 1972 1973
      before do
        stub_container_registry_config(enabled: false)
      end
1974

1975
      it 'does not have image tags' do
1976 1977 1978
        expect(project).not_to have_container_registry_tags
      end

1979
      it 'does not check root repository tags' do
1980 1981 1982 1983
        expect(project).not_to receive(:full_path)
        expect(project).not_to have_container_registry_tags
      end

1984
      it 'iterates through container repositories' do
1985 1986 1987 1988 1989 1990
        expect(project).to receive(:container_repositories)
        expect(project).not_to have_container_registry_tags
      end
    end
  end

1991
  describe '#ci_config_path=' do
1992
    let(:project) { create(:project) }
1993 1994

    it 'sets nil' do
1995
      project.update!(ci_config_path: nil)
1996

1997
      expect(project.ci_config_path).to be_nil
1998 1999 2000
    end

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

2003
      expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
2004 2005
    end

2006 2007
    it 'sets a string but removes all null characters' do
      project.update!(ci_config_path: "f\0oo/\0/.gitlab_ci.yml")
2008

2009
      expect(project.ci_config_path).to eq('foo//.gitlab_ci.yml')
2010 2011 2012
    end
  end

2013
  describe '#latest_successful_build_for_ref' do
2014
    let(:project) { create(:project, :repository) }
2015
    let(:pipeline) { create_pipeline(project) }
Lin Jen-Shin's avatar
Lin Jen-Shin committed
2016

2017
    it_behaves_like 'latest successful build for sha or ref'
Lin Jen-Shin's avatar
Lin Jen-Shin committed
2018

2019
    subject { project.latest_successful_build_for_ref(build_name) }
2020

2021 2022
    context 'with a specified ref' do
      let(:build) { create_build }
2023

2024
      subject { project.latest_successful_build_for_ref(build.name, project.default_branch) }
2025

2026
      it { is_expected.to eq(build) }
2027 2028
    end
  end
2029

2030 2031 2032
  describe '#latest_successful_build_for_sha' do
    let(:project) { create(:project, :repository) }
    let(:pipeline) { create_pipeline(project) }
2033

2034
    it_behaves_like 'latest successful build for sha or ref'
2035

2036
    subject { project.latest_successful_build_for_sha(build_name, project.commit.sha) }
2037 2038
  end

2039
  describe '#latest_successful_build_for_ref!' do
2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051
    let(:project) { create(:project, :repository) }
    let(:pipeline) { create_pipeline(project) }

    context 'with many builds' do
      it 'gives the latest builds from latest pipeline' do
        pipeline1 = create_pipeline(project)
        pipeline2 = create_pipeline(project)
        create_build(pipeline1, 'test')
        create_build(pipeline1, 'test2')
        build1_p2 = create_build(pipeline2, 'test')
        create_build(pipeline2, 'test2')

2052
        expect(project.latest_successful_build_for_ref!(build1_p2.name))
2053 2054 2055 2056 2057 2058 2059 2060 2061
          .to eq(build1_p2)
      end
    end

    context 'with succeeded pipeline' do
      let!(:build) { create_build }

      context 'standalone pipeline' do
        it 'returns builds for ref for default_branch' do
2062
          expect(project.latest_successful_build_for_ref!(build.name))
2063 2064 2065 2066
            .to eq(build)
        end

        it 'returns exception if the build cannot be found' do
2067
          expect { project.latest_successful_build_for_ref!(build.name, 'TAIL') }
2068
            .to raise_error(ActiveRecord::RecordNotFound)
2069
        end
2070 2071
      end

Lin Jen-Shin's avatar
Lin Jen-Shin committed
2072
      context 'with some pending pipeline' do
2073
        before do
2074
          create_build(create_pipeline(project, 'pending'))
2075 2076
        end

Lin Jen-Shin's avatar
Lin Jen-Shin committed
2077
        it 'gives the latest build from latest pipeline' do
2078
          expect(project.latest_successful_build_for_ref!(build.name))
2079
            .to eq(build)
2080
        end
2081 2082 2083 2084
      end
    end

    context 'with pending pipeline' do
2085
      it 'returns empty relation' do
2086
        pipeline.update(status: 'pending')
2087
        pending_build = create_build(pipeline)
2088

2089
        expect { project.latest_successful_build_for_ref!(pending_build.name) }
2090
          .to raise_error(ActiveRecord::RecordNotFound)
2091 2092 2093 2094
      end
    end
  end

2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130
  describe '#import_status' do
    context 'with import_state' do
      it 'returns the right status' do
        project = create(:project, :import_started)

        expect(project.import_status).to eq("started")
      end
    end

    context 'without import_state' do
      it 'returns none' do
        project = create(:project)

        expect(project.import_status).to eq('none')
      end
    end
  end

  describe '#human_import_status_name' do
    context 'with import_state' do
      it 'returns the right human import status' do
        project = create(:project, :import_started)

        expect(project.human_import_status_name).to eq('started')
      end
    end

    context 'without import_state' do
      it 'returns none' do
        project = create(:project)

        expect(project.human_import_status_name).to eq('none')
      end
    end
  end

2131
  describe '#add_import_job' do
2132 2133
    let(:import_jid) { '123' }

2134
    context 'forked' do
2135 2136 2137 2138 2139 2140
      let(:forked_from_project) { create(:project, :repository) }
      let(:project) { create(:project) }

      before do
        fork_project(forked_from_project, nil, target_project: project)
      end
2141 2142

      it 'schedules a RepositoryForkWorker job' do
2143
        expect(RepositoryForkWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
2144

2145
        expect(project.add_import_job).to eq(import_jid)
2146
      end
2147 2148 2149 2150 2151 2152 2153 2154 2155

      context 'without repository' do
        it 'schedules RepositoryImportWorker' do
          project = create(:project, import_url: generate(:url))

          expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
          expect(project.add_import_job).to eq(import_jid)
        end
      end
2156 2157 2158 2159
    end

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

2162 2163
        expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
        expect(project.add_import_job).to eq(import_jid)
2164 2165 2166 2167
      end
    end
  end

Rémy Coutable's avatar
Rémy Coutable committed
2168
  describe '#gitlab_project_import?' do
2169
    subject(:project) { build(:project, import_type: 'gitlab_project') }
Rémy Coutable's avatar
Rémy Coutable committed
2170 2171 2172 2173 2174

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

  describe '#gitea_import?' do
2175
    subject(:project) { build(:project, import_type: 'gitea') }
Rémy Coutable's avatar
Rémy Coutable committed
2176 2177 2178 2179

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

2180
  describe '#has_remote_mirror?' do
2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192
    let(:project) { create(:project, :remote_mirror, :import_started) }
    subject { project.has_remote_mirror? }

    before do
      allow_any_instance_of(RemoteMirror).to receive(:refresh_remote)
    end

    it 'returns true when a remote mirror is enabled' do
      is_expected.to be_truthy
    end

    it 'returns false when remote mirror is disabled' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
2193
      project.remote_mirrors.first.update(enabled: false)
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222

      is_expected.to be_falsy
    end
  end

  describe '#update_remote_mirrors' do
    let(:project) { create(:project, :remote_mirror, :import_started) }
    delegate :update_remote_mirrors, to: :project

    before do
      allow_any_instance_of(RemoteMirror).to receive(:refresh_remote)
    end

    it 'syncs enabled remote mirror' do
      expect_any_instance_of(RemoteMirror).to receive(:sync)

      update_remote_mirrors
    end

    it 'does nothing when remote mirror is disabled globally and not overridden' do
      stub_application_setting(mirror_available: false)
      project.remote_mirror_available_overridden = false

      expect_any_instance_of(RemoteMirror).not_to receive(:sync)

      update_remote_mirrors
    end

    it 'does not sync disabled remote mirrors' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
2223
      project.remote_mirrors.first.update(enabled: false)
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256

      expect_any_instance_of(RemoteMirror).not_to receive(:sync)

      update_remote_mirrors
    end
  end

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

    context 'when remote mirror global setting is enabled' do
      it 'returns true' do
        expect(project.remote_mirror_available?).to be(true)
      end
    end

    context 'when remote mirror global setting is disabled' do
      before do
        stub_application_setting(mirror_available: false)
      end

      it 'returns true when overridden' do
        project.remote_mirror_available_overridden = true

        expect(project.remote_mirror_available?).to be(true)
      end

      it 'returns false when not overridden' do
        expect(project.remote_mirror_available?).to be(false)
      end
    end
  end

2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271
  describe '#mark_stuck_remote_mirrors_as_failed!' do
    it 'fails stuck remote mirrors' do
      project = create(:project, :repository, :remote_mirror)

      project.remote_mirrors.first.update(
        update_status: :started,
        last_update_started_at: 2.days.ago
      )

      expect do
        project.mark_stuck_remote_mirrors_as_failed!
      end.to change { project.remote_mirrors.stuck.count }.from(1).to(0)
    end
  end

2272
  describe '#ancestors_upto' do
2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284
    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
2285 2286 2287 2288 2289 2290 2291 2292 2293 2294

    describe 'with hierarchy_order' do
      it 'returns ancestors ordered by descending hierarchy' do
        expect(project.ancestors_upto(hierarchy_order: :desc)).to eq([parent, child, child2])
      end

      it 'can be used with upto option' do
        expect(project.ancestors_upto(parent, hierarchy_order: :desc)).to eq([child, child2])
      end
    end
2295 2296
  end

2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
  describe '#root_ancestor' do
    let(:project) { create(:project) }

    subject { project.root_ancestor }

    it { is_expected.to eq(project.namespace) }

    context 'in a group' do
      let(:group) { create(:group) }
      let(:project) { create(:project, group: group) }

      it { is_expected.to eq(group) }
    end

2311
    context 'in a nested group' do
2312 2313 2314 2315 2316 2317
      let(:root) { create(:group) }
      let(:child) { create(:group, parent: root) }
      let(:project) { create(:project, group: child) }

      it { is_expected.to eq(root) }
    end
2318 2319
  end

2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370
  describe '#emails_disabled?' do
    let(:project) { create(:project, emails_disabled: false) }

    context 'emails disabled in group' do
      it 'returns true' do
        allow(project.namespace).to receive(:emails_disabled?) { true }

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

    context 'emails enabled in group' do
      before do
        allow(project.namespace).to receive(:emails_disabled?) { false }
      end

      it 'returns false' do
        expect(project.emails_disabled?).to be_falsey
      end

      it 'returns true' do
        project.update_attribute(:emails_disabled, true)

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

    context 'when :emails_disabled feature flag is off' do
      before do
        stub_feature_flags(emails_disabled: false)
      end

      context 'emails disabled in group' do
        it 'returns false' do
          allow(project.namespace).to receive(:emails_disabled?) { true }

          expect(project.emails_disabled?).to be_falsey
        end
      end

      context 'emails enabled in group' do
        it 'returns false' do
          allow(project.namespace).to receive(:emails_disabled?) { false }
          project.update_attribute(:emails_disabled, true)

          expect(project.emails_disabled?).to be_falsey
        end
      end
    end
  end

2371
  describe '#lfs_enabled?' do
2372
    let(:project) { create(:project) }
2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432

    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

2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444
  describe '#daily_statistics_enabled?' do
    it { is_expected.to be_daily_statistics_enabled }

    context 'when :project_daily_statistics is disabled for the project' do
      before do
        stub_feature_flags(project_daily_statistics: { thing: subject, enabled: false })
      end

      it { is_expected.not_to be_daily_statistics_enabled }
    end
  end

2445
  describe '#change_head' do
2446
    let(:project) { create(:project, :repository) }
2447

2448 2449 2450 2451 2452
    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

2453
    it 'calls the before_change_head and after_change_head methods' do
2454
      expect(project.repository).to receive(:before_change_head)
2455 2456
      expect(project.repository).to receive(:after_change_head)

2457 2458 2459
      project.change_head(project.default_branch)
    end

2460 2461 2462 2463 2464 2465
    it 'updates commit count' do
      expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:commit_count])

      project.change_head(project.default_branch)
    end

2466 2467 2468 2469 2470 2471 2472 2473 2474 2475
    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
2476

2477
  context 'forks' do
2478 2479
    include ProjectForksHelper

2480 2481 2482 2483 2484
    let(:project) { create(:project, :public) }
    let!(:forked_project) { fork_project(project) }

    describe '#fork_network' do
      it 'includes a fork of the project' do
2485
        expect(project.fork_network.projects).to include(forked_project)
2486 2487 2488 2489 2490
      end

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

2491
        expect(project.fork_network.projects).to include(other_fork)
2492 2493 2494 2495 2496
      end

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

2497
        expect(forked_project.fork_network.projects).to include(other_fork)
2498 2499 2500
      end

      it 'includes the base project' do
2501
        expect(forked_project.fork_network.projects).to include(project.reload)
2502 2503 2504 2505 2506 2507 2508 2509
      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

2510
      it 'is true for a fork of a fork' do
2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527
        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
2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540

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

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

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

        expect(second_fork.fork_source).to eq(project)
      end
2541 2542 2543 2544

      it 'returns nil if it is the root of the fork network' do
        expect(project.fork_source).to be_nil
      end
2545
    end
2546

2547 2548 2549 2550 2551 2552
    describe '#forks' do
      it 'includes direct forks of the project' do
        expect(project.forks).to contain_exactly(forked_project)
      end
    end

2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569
    describe '#lfs_storage_project' do
      it 'returns self for non-forks' do
        expect(project.lfs_storage_project).to eq project
      end

      it 'returns the fork network root for forks' do
        second_fork = fork_project(forked_project)

        expect(second_fork.lfs_storage_project).to eq project
      end

      it 'returns self when fork_source is nil' do
        expect(forked_project).to receive(:fork_source).and_return(nil)

        expect(forked_project.lfs_storage_project).to eq forked_project
      end
    end
2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585

    describe '#all_lfs_objects' do
      let(:lfs_object) { create(:lfs_object) }

      before do
        project.lfs_objects << lfs_object
      end

      it 'returns the lfs object for a project' do
        expect(project.all_lfs_objects).to contain_exactly(lfs_object)
      end

      it 'returns the lfs object for a fork' do
        expect(forked_project.all_lfs_objects).to contain_exactly(lfs_object)
      end
    end
2586 2587
  end

2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
  describe '#set_repository_read_only!' do
    let(:project) { create(:project) }

    it 'returns true when there is no existing git transfer in progress' do
      expect(project.set_repository_read_only!).to be_truthy
    end

    it 'returns false when there is an existing git transfer in progress' do
      allow(project).to receive(:git_transfer_in_progress?) { true }

      expect(project.set_repository_read_only!).to be_falsey
    end
  end

2602 2603 2604 2605 2606 2607 2608 2609 2610 2611
  describe '#set_repository_writable!' do
    it 'sets repository_read_only to false' do
      project = create(:project, :read_only)

      expect { project.set_repository_writable! }
        .to change(project, :repository_read_only)
        .from(true).to(false)
    end
  end

2612
  describe '#pushes_since_gc' do
2613
    let(:project) { create(:project) }
2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634

    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
2635
    let(:project) { create(:project) }
2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648

    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
2649
    let(:project) { create(:project) }
2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662

    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
2663

2664
  describe '#deployment_variables' do
2665 2666
    let(:project) { create(:project) }
    let(:environment) { 'production' }
2667

2668 2669 2670 2671 2672
    subject { project.deployment_variables(environment: environment) }

    before do
      expect(project).to receive(:deployment_platform).with(environment: environment)
        .and_return(deployment_platform)
2673 2674
    end

2675 2676
    context 'when project has no deployment platform' do
      let(:deployment_platform) { nil }
2677

2678
      it { is_expected.to eq [] }
2679 2680
    end

2681 2682 2683
    context 'when project has a deployment platform' do
      let(:platform_variables) { %w(platform variables) }
      let(:deployment_platform) { double }
2684

2685 2686 2687 2688
      before do
        expect(deployment_platform).to receive(:predefined_variables)
          .with(project: project, environment_name: environment)
          .and_return(platform_variables)
2689 2690
      end

2691
      it { is_expected.to eq platform_variables }
2692 2693 2694
    end
  end

2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716
  describe '#default_environment' do
    let(:project) { create(:project) }

    it 'returns production environment when it exists' do
      production = create(:environment, name: "production", project: project)
      create(:environment, name: 'staging', project: project)

      expect(project.default_environment).to eq(production)
    end

    it 'returns first environment when no production environment exists' do
      create(:environment, name: 'staging', project: project)
      create(:environment, name: 'foo', project: project)

      expect(project.default_environment).to eq(project.environments.first)
    end

    it 'returns nil when no available environment exists' do
      expect(project.default_environment).to be_nil
    end
  end

2717
  describe '#ci_variables_for' do
2718
    let(:project) { create(:project) }
2719
    let(:environment_scope) { '*' }
2720

2721
    let!(:ci_variable) do
2722
      create(:ci_variable, value: 'secret', project: project, environment_scope: environment_scope)
2723 2724 2725 2726 2727 2728
    end

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

2729
    subject { project.reload.ci_variables_for(ref: 'ref') }
Lin Jen-Shin's avatar
Lin Jen-Shin committed
2730 2731 2732 2733 2734

    before do
      stub_application_setting(
        default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end
2735 2736 2737

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
2738
        is_expected.to contain_exactly(ci_variable, protected_variable)
2739 2740 2741 2742
      end
    end

    context 'when the ref is not protected' do
2743 2744 2745 2746
      before do
        allow(project).to receive(:protected_for?).with('ref').and_return(false)
      end

2747 2748
      it 'contains only the CI variables' do
        is_expected.to contain_exactly(ci_variable)
2749 2750 2751
      end
    end

2752 2753
    context 'when the ref is a protected branch' do
      before do
2754
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
2755
      end
2756 2757 2758 2759 2760 2761

      it_behaves_like 'ref is protected'
    end

    context 'when the ref is a protected tag' do
      before do
2762
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
2763 2764 2765
      end

      it_behaves_like 'ref is protected'
2766
    end
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 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856

    context 'when environment name is specified' do
      let(:environment) { 'review/name' }

      subject do
        project.ci_variables_for(ref: 'ref', environment: environment)
      end

      context 'when environment scope is exactly matched' do
        let(:environment_scope) { 'review/name' }

        it { is_expected.to contain_exactly(ci_variable) }
      end

      context 'when environment scope is matched by wildcard' do
        let(:environment_scope) { 'review/*' }

        it { is_expected.to contain_exactly(ci_variable) }
      end

      context 'when environment scope does not match' do
        let(:environment_scope) { 'review/*/special' }

        it { is_expected.not_to contain_exactly(ci_variable) }
      end

      context 'when environment scope has _' do
        let(:environment_scope) { '*_*' }

        it 'does not treat it as wildcard' do
          is_expected.not_to contain_exactly(ci_variable)
        end

        context 'when environment name contains underscore' do
          let(:environment) { 'foo_bar/test' }
          let(:environment_scope) { 'foo_bar/*' }

          it 'matches literally for _' do
            is_expected.to contain_exactly(ci_variable)
          end
        end
      end

      # The environment name and scope cannot have % at the moment,
      # but we're considering relaxing it and we should also make sure
      # it doesn't break in case some data sneaked in somehow as we're
      # not checking this integrity in database level.
      context 'when environment scope has %' do
        it 'does not treat it as wildcard' do
          ci_variable.update_attribute(:environment_scope, '*%*')

          is_expected.not_to contain_exactly(ci_variable)
        end

        context 'when environment name contains a percent' do
          let(:environment) { 'foo%bar/test' }

          it 'matches literally for _' do
            ci_variable.update(environment_scope: 'foo%bar/*')

            is_expected.to contain_exactly(ci_variable)
          end
        end
      end

      context 'when variables with the same name have different environment scopes' do
        let!(:partially_matched_variable) do
          create(:ci_variable,
                 key: ci_variable.key,
                 value: 'partial',
                 environment_scope: 'review/*',
                 project: project)
        end

        let!(:perfectly_matched_variable) do
          create(:ci_variable,
                 key: ci_variable.key,
                 value: 'prefect',
                 environment_scope: 'review/name',
                 project: project)
        end

        it 'puts variables matching environment scope more in the end' do
          is_expected.to eq(
            [ci_variable,
             partially_matched_variable,
             perfectly_matched_variable])
        end
      end
    end
2857 2858
  end

2859
  describe '#any_lfs_file_locks?', :request_store do
2860
    set(:project) { create(:project) }
2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874

    it 'returns false when there are no LFS file locks' do
      expect(project.any_lfs_file_locks?).to be_falsey
    end

    it 'returns a cached true when there are LFS file locks' do
      create(:lfs_file_lock, project: project)

      expect(project.lfs_file_locks).to receive(:any?).once.and_call_original

      2.times { expect(project.any_lfs_file_locks?).to be_truthy }
    end
  end

2875
  describe '#protected_for?' do
2876
    let(:project) { create(:project, :repository) }
2877

2878
    subject { project.protected_for?(ref) }
2879

2880
    shared_examples 'ref is not protected' do
2881 2882 2883 2884 2885 2886
      before do
        stub_application_setting(
          default_branch_protection: Gitlab::Access::PROTECTION_NONE)
      end

      it 'returns false' do
2887
        is_expected.to be false
2888 2889 2890
      end
    end

2891
    shared_examples 'ref is protected branch' do
2892
      before do
2893
        create(:protected_branch, name: 'master', project: project)
2894 2895 2896
      end

      it 'returns true' do
2897
        is_expected.to be true
2898 2899 2900
      end
    end

2901
    shared_examples 'ref is protected tag' do
2902
      before do
2903
        create(:protected_tag, name: 'v1.0.0', project: project)
2904 2905 2906
      end

      it 'returns true' do
2907
        is_expected.to be true
2908 2909
      end
    end
2910

2911 2912
    context 'when ref is nil' do
      let(:ref) { nil }
2913

2914
      it 'returns false' do
2915
        is_expected.to be false
2916
      end
2917 2918 2919 2920 2921
    end

    context 'when ref is ref name' do
      context 'when ref is ambiguous' do
        let(:ref) { 'ref' }
2922 2923

        before do
2924 2925
          project.repository.add_branch(project.creator, 'ref', 'master')
          project.repository.add_tag(project.creator, 'ref', 'master')
2926 2927
        end

2928 2929
        it 'raises an error' do
          expect { subject }.to raise_error(Repository::AmbiguousRefError)
2930 2931
        end
      end
2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949

      context 'when the ref is not protected' do
        let(:ref) { 'master' }

        it_behaves_like 'ref is not protected'
      end

      context 'when the ref is a protected branch' do
        let(:ref) { 'master' }

        it_behaves_like 'ref is protected branch'
      end

      context 'when the ref is a protected tag' do
        let(:ref) { 'v1.0.0' }

        it_behaves_like 'ref is protected tag'
      end
2950 2951 2952 2953 2954 2955 2956 2957

      context 'when ref does not exist' do
        let(:ref) { 'something' }

        it 'returns false' do
          is_expected.to be false
        end
      end
2958 2959
    end

2960 2961 2962
    context 'when ref is full ref' do
      context 'when the ref is not protected' do
        let(:ref) { 'refs/heads/master' }
2963

2964
        it_behaves_like 'ref is not protected'
2965 2966
      end

2967 2968 2969 2970 2971 2972 2973 2974 2975 2976
      context 'when the ref is a protected branch' do
        let(:ref) { 'refs/heads/master' }

        it_behaves_like 'ref is protected branch'
      end

      context 'when the ref is a protected tag' do
        let(:ref) { 'refs/tags/v1.0.0' }

        it_behaves_like 'ref is protected tag'
2977 2978
      end

2979 2980 2981
      context 'when branch ref name is a full tag ref' do
        let(:ref) { 'refs/tags/something' }

2982
        before do
2983
          project.repository.add_branch(project.creator, ref, 'master')
2984 2985
        end

2986 2987
        context 'when ref is not protected' do
          it 'returns false' do
2988
            is_expected.to be false
2989 2990 2991 2992 2993 2994 2995 2996 2997
          end
        end

        context 'when ref is a protected branch' do
          before do
            create(:protected_branch, name: 'refs/tags/something', project: project)
          end

          it 'returns true' do
2998
            is_expected.to be true
2999
          end
3000 3001
        end
      end
3002 3003 3004 3005 3006 3007 3008

      context 'when ref does not exist' do
        let(:ref) { 'refs/heads/something' }

        it 'returns false' do
          is_expected.to be false
        end
3009 3010 3011 3012
      end
    end
  end

3013
  describe '#update_project_statistics' do
3014
    let(:project) { create(:project) }
3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032

    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

3033
  describe 'inside_path' do
3034 3035 3036
    let!(:project1) { create(:project, namespace: create(:namespace, path: 'name_pace')) }
    let!(:project2) { create(:project) }
    let!(:project3) { create(:project, namespace: create(:namespace, path: 'namespace')) }
3037
    let!(:path) { project1.namespace.full_path }
3038

3039
    it 'returns correct project' do
3040
      expect(described_class.inside_path(path)).to eq([project1])
3041
    end
3042 3043
  end

Douwe Maan's avatar
Douwe Maan committed
3044
  describe '#route_map_for' do
3045
    let(:project) { create(:project, :repository) }
Douwe Maan's avatar
Douwe Maan committed
3046 3047 3048 3049 3050 3051 3052 3053
    let(:route_map) do
      <<-MAP.strip_heredoc
      - source: /source/(.*)/
        public: '\\1'
      MAP
    end

    before do
3054
      project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
Douwe Maan's avatar
Douwe Maan committed
3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081
    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
3082
    let(:project) { create(:project, :repository) }
Douwe Maan's avatar
Douwe Maan committed
3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106
    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
3107 3108 3109 3110 3111 3112 3113 3114 3115 3116

      it 'returns a public path with a leading slash unmodified' do
        route_map = Gitlab::RouteMap.new(<<-MAP.strip_heredoc)
          - source: 'source/file.html'
            public: '/public/file'
        MAP
        allow(project).to receive(:route_map_for).with(sha).and_return(route_map)

        expect(project.public_path_for_source_path('source/file.html', sha)).to eq('/public/file')
      end
Douwe Maan's avatar
Douwe Maan committed
3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129
    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

3130
  describe '#parent' do
3131
    let(:project) { create(:project) }
3132 3133 3134 3135

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

3136 3137 3138 3139 3140 3141
  describe '#parent_id' do
    let(:project) { create(:project) }

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

3142
  describe '#parent_changed?' do
3143
    let(:project) { create(:project) }
3144

3145 3146 3147
    before do
      project.namespace_id = 7
    end
3148 3149 3150 3151

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

3152 3153 3154
  def enable_lfs
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
  end
3155

3156
  describe '#pages_url' do
3157 3158
    let(:group) { create(:group, name: 'Group') }
    let(:nested_group) { create(:group, parent: group) }
3159 3160 3161 3162 3163 3164 3165 3166 3167
    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

3168
    context 'top-level group' do
3169
      let(:project) { create(:project, namespace: group, name: project_name) }
3170

3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181
      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
3182 3183
    end

3184
    context 'nested group' do
3185
      let(:project) { create(:project, namespace: nested_group, name: project_name) }
3186
      let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" }
3187

3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198
      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
3199 3200
    end
  end
3201 3202

  describe '#http_url_to_repo' do
3203
    let(:project) { create(:project) }
3204

3205 3206 3207
    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('@')
3208 3209
    end
  end
3210

3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221
  describe '#lfs_http_url_to_repo' do
    let(:project) { create(:project) }

    it 'returns the url to the repo without a username' do
      lfs_http_url_to_repo = project.lfs_http_url_to_repo('operation_that_doesnt_matter')

      expect(lfs_http_url_to_repo).to eq("#{project.web_url}.git")
      expect(lfs_http_url_to_repo).not_to include('@')
    end
  end

3222
  describe '#pipeline_status' do
3223
    let(:project) { create(:project, :repository) }
3224
    it 'builds a pipeline status' do
3225
      expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
3226 3227 3228 3229 3230 3231
    end

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

  describe '#append_or_update_attribute' do
3234
    let(:project) { create(:project) }
3235 3236

    it 'shows full error updating an invalid MR' do
3237
      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
3238
        .to raise_error(ActiveRecord::RecordInvalid, /Failed to set merge_requests:/)
3239 3240
    end

3241
    it 'updates the project successfully' do
3242 3243
      merge_request = create(:merge_request, target_project: project, source_project: project)

3244 3245
      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
        .not_to raise_error
3246 3247
    end
  end
3248

3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266
  describe '#update' do
    let(:project) { create(:project) }

    it 'validates the visibility' do
      expect(project).to receive(:visibility_level_allowed_as_fork).and_call_original
      expect(project).to receive(:visibility_level_allowed_by_group).and_call_original

      project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
    end

    it 'does not validate the visibility' do
      expect(project).not_to receive(:visibility_level_allowed_as_fork).and_call_original
      expect(project).not_to receive(:visibility_level_allowed_by_group).and_call_original

      project.update(updated_at: Time.now)
    end
  end

3267 3268
  describe '#last_repository_updated_at' do
    it 'sets to created_at upon creation' do
3269
      project = create(:project, created_at: 2.hours.ago)
3270 3271 3272 3273

      expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
    end
  end
3274 3275 3276 3277 3278

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

    let!(:private_project) do
3279
      create(:project, :private, creator: user, namespace: user.namespace)
3280 3281
    end

3282
    let!(:public_project) { create(:project, :public) }
3283 3284 3285

    context 'with a user' do
      let(:projects) do
3286
        described_class.all.public_or_visible_to_user(user)
3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299
      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
3300
        projects = described_class.all.public_or_visible_to_user
3301 3302 3303 3304 3305

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

3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323
  describe '.ids_with_milestone_available_for' do
    let!(:user) { create(:user) }

    it 'returns project ids with milestones available for user' do
      project_1 = create(:project, :public, :merge_requests_disabled, :issues_disabled)
      project_2 = create(:project, :public, :merge_requests_disabled)
      project_3 = create(:project, :public, :issues_disabled)
      project_4 = create(:project, :public)
      project_4.project_feature.update(issues_access_level: ProjectFeature::PRIVATE, merge_requests_access_level: ProjectFeature::PRIVATE )

      project_ids = described_class.ids_with_milestone_available_for(user).pluck(:id)

      expect(project_ids).to include(project_2.id, project_3.id)
      expect(project_ids).not_to include(project_1.id, project_4.id)
    end
  end

3324
  describe '.with_feature_available_for_user' do
3325 3326
    let(:user) { create(:user) }
    let(:feature) { MergeRequest }
3327 3328 3329

    subject { described_class.with_feature_available_for_user(feature, user) }

3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362
    shared_examples 'feature disabled' do
      let(:project) { create(:project, :public, :merge_requests_disabled) }

      it 'does not return projects with the project feature disabled' do
        is_expected.not_to include(project)
      end
    end

    shared_examples 'feature public' do
      let(:project) { create(:project, :public, :merge_requests_public) }

      it 'returns projects with the project feature public' do
        is_expected.to include(project)
      end
    end

    shared_examples 'feature enabled' do
      let(:project) { create(:project, :public, :merge_requests_enabled) }

      it 'returns projects with the project feature enabled' do
        is_expected.to include(project)
      end
    end

    shared_examples 'feature access level is nil' do
      let(:project) { create(:project, :public) }

      it 'returns projects with the project feature access level nil' do
        project.project_feature.update(merge_requests_access_level: nil)

        is_expected.to include(project)
      end
    end
3363

3364
    context 'with user' do
3365 3366 3367 3368
      before do
        project.add_guest(user)
      end

3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379
      it_behaves_like 'feature disabled'
      it_behaves_like 'feature public'
      it_behaves_like 'feature enabled'
      it_behaves_like 'feature access level is nil'

      context 'when feature is private' do
        let(:project) { create(:project, :public, :merge_requests_private) }

        context 'when user does not has access to the feature' do
          it 'does not return projects with the project feature private' do
            is_expected.not_to include(project)
3380 3381 3382
          end
        end

3383 3384 3385
        context 'when user has access to the feature' do
          it 'returns projects with the project feature private' do
            project.add_reporter(user)
3386 3387 3388 3389 3390

            is_expected.to include(project)
          end
        end
      end
3391
    end
3392

3393 3394
    context 'user is an admin' do
      let(:user) { create(:user, :admin) }
3395

3396 3397 3398 3399
      it_behaves_like 'feature disabled'
      it_behaves_like 'feature public'
      it_behaves_like 'feature enabled'
      it_behaves_like 'feature access level is nil'
3400

3401 3402
      context 'when feature is private' do
        let(:project) { create(:project, :public, :merge_requests_private) }
3403

3404 3405
        it 'returns projects with the project feature private' do
          is_expected.to include(project)
3406 3407 3408 3409
        end
      end
    end

3410 3411
    context 'without user' do
      let(:user) { nil }
3412

3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423
      it_behaves_like 'feature disabled'
      it_behaves_like 'feature public'
      it_behaves_like 'feature enabled'
      it_behaves_like 'feature access level is nil'

      context 'when feature is private' do
        let(:project) { create(:project, :public, :merge_requests_private) }

        it 'does not return projects with the project feature private' do
          is_expected.not_to include(project)
        end
3424 3425 3426 3427
      end
    end
  end

3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445
  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) }

3446
      it { is_expected.to be(true) }
3447 3448 3449
    end
  end

3450
  describe '#remove_private_deploy_keys' do
3451
    let!(:project) { create(:project) }
3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466

    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
3467
          another_project = create(:project)
3468 3469
          create(:deploy_keys_project, deploy_key: key, project: another_project)
        end
3470

3471 3472
        it 'does not remove the key' do
          project.remove_private_deploy_keys
3473

3474 3475 3476 3477 3478 3479 3480 3481
          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) }
3482

3483 3484
      it 'does not remove the key' do
        project.remove_private_deploy_keys
3485

3486 3487
        expect(project.deploy_keys).to include(key)
      end
3488 3489
    end
  end
3490

3491 3492
  describe '#remove_pages' do
    let(:project) { create(:project) }
3493
    let(:namespace) { project.namespace }
3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504
    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

3505 3506 3507 3508 3509 3510 3511 3512
    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

3513
    it 'is a no-op when there is no namespace' do
3514 3515
      project.namespace.delete
      project.reload
3516 3517 3518 3519 3520 3521

      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
3522 3523 3524 3525 3526 3527

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

      project.destroy
    end
3528 3529
  end

3530
  describe '#remove_export' do
3531 3532
    let(:project) { create(:project, :with_export) }

3533
    it 'removes the export' do
3534 3535
      project.remove_exports

3536
      expect(project.export_file_exists?).to be_falsey
3537 3538 3539
    end
  end

3540 3541 3542 3543
  describe '#forks_count' do
    it 'returns the number of forks' do
      project = build(:project)

Francisco Lopez's avatar
Francisco Lopez committed
3544
      expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1)
3545 3546 3547 3548

      expect(project.forks_count).to eq(1)
    end
  end
3549

3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576
  describe '#git_transfer_in_progress?' do
    let(:project) { build(:project) }

    subject { project.git_transfer_in_progress? }

    it 'returns false when repo_reference_count and wiki_reference_count are 0' do
      allow(project).to receive(:repo_reference_count) { 0 }
      allow(project).to receive(:wiki_reference_count) { 0 }

      expect(subject).to be_falsey
    end

    it 'returns true when repo_reference_count is > 0' do
      allow(project).to receive(:repo_reference_count) { 2 }
      allow(project).to receive(:wiki_reference_count) { 0 }

      expect(subject).to be_truthy
    end

    it 'returns true when wiki_reference_count is > 0' do
      allow(project).to receive(:repo_reference_count) { 0 }
      allow(project).to receive(:wiki_reference_count) { 2 }

      expect(subject).to be_truthy
    end
  end

3577
  context 'legacy storage' do
3578
    set(:project) { create(:project, :repository, :legacy_storage) }
3579
    let(:gitlab_shell) { Gitlab::Shell.new }
3580
    let(:project_storage) { project.send(:storage) }
3581

3582 3583
    before do
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
3584
      stub_application_setting(hashed_storage_enabled: false)
3585 3586
    end

3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598
    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

3599
    describe '#ensure_storage_path_exists' do
3600
      it 'delegates to gitlab_shell to ensure namespace is created' do
3601
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, project.base_dir)
3602

3603
        project.ensure_storage_path_exists
3604 3605 3606
      end
    end

3607 3608
    describe '#legacy_storage?' do
      it 'returns true when storage_version is nil' do
3609
        project = build(:project, storage_version: nil)
3610 3611 3612

        expect(project.legacy_storage?).to be_truthy
      end
3613 3614 3615 3616 3617 3618 3619 3620 3621 3622

      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
3623
        expect(project.hashed_storage?(:repository)).to be_falsey
3624
      end
3625 3626
    end

3627 3628 3629 3630 3631
    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
3632 3633

    describe '#migrate_to_hashed_storage!' do
3634 3635
      let(:project) { create(:project, :empty_repo, :legacy_storage) }

3636 3637 3638 3639
      it 'returns true' do
        expect(project.migrate_to_hashed_storage!).to be_truthy
      end

3640 3641
      it 'does not run validation' do
        expect(project).not_to receive(:valid?)
3642 3643 3644 3645

        project.migrate_to_hashed_storage!
      end

3646
      it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do
3647
        Gitlab::ReferenceCounter.new(Gitlab::GlRepository::PROJECT.identifier_for_subject(project)).increase
3648

3649
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
3650 3651 3652 3653

        project.migrate_to_hashed_storage!
      end

3654
      it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do
3655
        Gitlab::ReferenceCounter.new(Gitlab::GlRepository::WIKI.identifier_for_subject(project)).increase
3656

3657
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
3658 3659 3660 3661

        project.migrate_to_hashed_storage!
      end

3662 3663
      it 'schedules HashedStorage::ProjectMigrateWorker' do
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_async).with(project.id)
3664 3665 3666 3667

        project.migrate_to_hashed_storage!
      end
    end
3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681

    describe '#rollback_to_legacy_storage!' do
      let(:project) { create(:project, :empty_repo, :legacy_storage) }

      it 'returns nil' do
        expect(project.rollback_to_legacy_storage!).to be_nil
      end

      it 'does not run validations' do
        expect(project).not_to receive(:valid?)

        project.rollback_to_legacy_storage!
      end
    end
3682 3683 3684
  end

  context 'hashed storage' do
3685
    set(:project) { create(:project, :repository, skip_disk_validation: true) }
3686
    let(:gitlab_shell) { Gitlab::Shell.new }
3687
    let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
3688 3689
    let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
    let(:hashed_path) { File.join(hashed_prefix, hash) }
3690

3691 3692 3693 3694 3695 3696 3697
    describe '#legacy_storage?' do
      it 'returns false' do
        expect(project.legacy_storage?).to be_falsey
      end
    end

    describe '#hashed_storage?' do
3698 3699
      it 'returns true if rolled out' do
        expect(project.hashed_storage?(:attachments)).to be_truthy
3700 3701
      end

3702 3703
      it 'returns false when not rolled out yet' do
        project.storage_version = 1
3704

3705
        expect(project.hashed_storage?(:attachments)).to be_falsey
3706 3707 3708
      end
    end

3709 3710
    describe '#base_dir' do
      it 'returns base_dir based on hash of project id' do
3711
        expect(project.base_dir).to eq(hashed_prefix)
3712 3713 3714 3715
      end
    end

    describe '#disk_path' do
3716
      it 'returns disk_path based on hash of project id' do
3717 3718 3719 3720
        expect(project.disk_path).to eq(hashed_path)
      end
    end

3721
    describe '#ensure_storage_path_exists' do
3722
      it 'delegates to gitlab_shell to ensure namespace is created' do
3723 3724
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)

3725
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, hashed_prefix)
3726

3727
        project.ensure_storage_path_exists
3728 3729 3730
      end
    end

3731 3732 3733 3734 3735
    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
3736 3737

    describe '#migrate_to_hashed_storage!' do
3738 3739
      let(:project) { create(:project, :repository, skip_disk_validation: true) }

3740 3741 3742 3743
      it 'returns nil' do
        expect(project.migrate_to_hashed_storage!).to be_nil
      end

3744
      it 'does not flag as read-only' do
3745 3746
        expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only }
      end
3747 3748

      context 'when partially migrated' do
3749
        it 'enqueues a job' do
3750 3751
          project = create(:project, storage_version: 1, skip_disk_validation: true)

3752
          Sidekiq::Testing.fake! do
3753
            expect { project.migrate_to_hashed_storage! }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1)
3754
          end
3755 3756
        end
      end
3757
    end
3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777

    describe '#rollback_to_legacy_storage!' do
      let(:project) { create(:project, :repository, skip_disk_validation: true) }

      it 'returns true' do
        expect(project.rollback_to_legacy_storage!).to be_truthy
      end

      it 'does not run validations' do
        expect(project).not_to receive(:valid?)

        project.rollback_to_legacy_storage!
      end

      it 'does not flag as read-only' do
        expect { project.rollback_to_legacy_storage! }.not_to change { project.repository_read_only }
      end

      it 'enqueues a job' do
        Sidekiq::Testing.fake! do
3778
          expect { project.rollback_to_legacy_storage! }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1)
3779 3780 3781
        end
      end
    end
3782 3783
  end

3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806
  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

3807 3808
      it "CI is available" do
        expect(project).to have_ci
3809 3810
      end

3811
      context 'when auto devops is disabled' do
3812
        before do
3813
          stub_application_setting(auto_devops_enabled: false)
3814 3815
        end

3816 3817
        it "CI is not available" do
          expect(project).not_to have_ci
3818 3819 3820 3821 3822 3823
        end
      end
    end
  end

  describe '#auto_devops_enabled?' do
3824 3825 3826 3827 3828
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

3829 3830 3831 3832
    set(:project) { create(:project) }

    subject { project.auto_devops_enabled? }

3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848
    context 'when explicitly enabled' do
      before do
        create(:project_auto_devops, project: project)
      end

      it { is_expected.to be_truthy }
    end

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

      it { is_expected.to be_falsey }
    end

3849 3850 3851 3852 3853
    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

3854
      it { is_expected.to be_truthy }
3855 3856 3857 3858 3859 3860 3861 3862
    end

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

      it { is_expected.to be_falsey }
3863 3864 3865 3866 3867 3868

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

3869
        it { is_expected.to be_truthy }
3870 3871 3872 3873
      end

      context 'when explicitly disabled' do
        before do
3874
          create(:project_auto_devops, :disabled, project: project)
3875 3876
        end

3877
        it { is_expected.to be_falsey }
3878 3879 3880
      end
    end

3881 3882 3883 3884 3885 3886 3887
    context 'when force_autodevops_on_by_default is enabled for the project' do
      it { is_expected.to be_truthy }
    end

    context 'with group parents' do
      let(:instance_enabled) { true }

3888
      before do
3889 3890
        stub_application_setting(auto_devops_enabled: instance_enabled)
        project.update!(namespace: parent_group)
3891 3892
      end

3893 3894
      context 'when enabled on parent' do
        let(:parent_group) { create(:group, :auto_devops_enabled) }
3895

3896 3897
        context 'when auto devops instance enabled' do
          it { is_expected.to be_truthy }
3898 3899
        end

3900 3901 3902 3903 3904
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_truthy }
        end
3905 3906
      end

3907 3908 3909 3910 3911
      context 'when disabled on parent' do
        let(:parent_group) { create(:group, :auto_devops_disabled) }

        context 'when auto devops instance enabled' do
          it { is_expected.to be_falsy }
3912
        end
3913

3914 3915 3916 3917 3918 3919 3920
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_falsy }
        end
      end

3921
      context 'when enabled on root parent' do
3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940
        let(:parent_group) { create(:group, parent: create(:group, :auto_devops_enabled)) }

        context 'when auto devops instance enabled' do
          it { is_expected.to be_truthy }
        end

        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_truthy }
        end

        context 'when explicitly disabled on parent' do
          let(:parent_group) { create(:group, :auto_devops_disabled, parent: create(:group, :auto_devops_enabled)) }

          it { is_expected.to be_falsy }
        end
      end

3941
      context 'when disabled on root parent' do
3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958
        let(:parent_group) { create(:group, parent: create(:group, :auto_devops_disabled)) }

        context 'when auto devops instance enabled' do
          it { is_expected.to be_falsy }
        end

        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_falsy }
        end

        context 'when explicitly disabled on parent' do
          let(:parent_group) { create(:group, :auto_devops_disabled, parent: create(:group, :auto_devops_enabled)) }

          it { is_expected.to be_falsy }
        end
3959 3960 3961 3962
      end
    end
  end

3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004
  describe '#has_auto_devops_implicitly_enabled?' do
    set(:project) { create(:project) }

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

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

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

      it 'auto devops is implicitly disabled' do
        expect(project).to have_auto_devops_implicitly_enabled
      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_enabled
        end
      end

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

        it 'does not have auto devops implicitly disabled' do
          expect(project).not_to have_auto_devops_implicitly_enabled
        end
      end
    end
4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021

    context 'when enabled on group' do
      it 'has auto devops implicitly enabled' do
        project.update(namespace: create(:group, :auto_devops_enabled))

        expect(project).to have_auto_devops_implicitly_enabled
      end
    end

    context 'when enabled on parent group' do
      it 'has auto devops implicitly enabled' do
        subgroup = create(:group, parent: create(:group, :auto_devops_enabled))
        project.update(namespace: subgroup)

        expect(project).to have_auto_devops_implicitly_enabled
      end
    end
4022 4023
  end

4024
  describe '#has_auto_devops_implicitly_disabled?' do
4025 4026
    set(:project) { create(:project) }

4027 4028 4029 4030 4031
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050
    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, enabled: true)
      end

      it 'does not have auto devops implicitly disabled' do
        expect(project).not_to have_auto_devops_implicitly_disabled
      end
    end
4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070

    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

4071 4072
      context 'when force_autodevops_on_by_default is enabled for the project' do
        before do
4073 4074
          create(:project_auto_devops, project: project, enabled: false)

4075 4076 4077 4078 4079 4080 4081 4082
          Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
        end

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

4083 4084 4085
      context 'when disabled on group' do
        it 'has auto devops implicitly disabled' do
          project.update!(namespace: create(:group, :auto_devops_disabled))
4086

4087
          expect(project).to have_auto_devops_implicitly_disabled
4088 4089 4090
        end
      end

4091 4092 4093 4094
      context 'when disabled on parent group' do
        it 'has auto devops implicitly disabled' do
          subgroup = create(:group, parent: create(:group, :auto_devops_disabled))
          project.update!(namespace: subgroup)
4095

4096
          expect(project).to have_auto_devops_implicitly_disabled
4097 4098 4099 4100 4101
        end
      end
    end
  end

4102 4103 4104 4105 4106 4107 4108 4109
  describe '#api_variables' do
    set(:project) { create(:project) }

    it 'exposes API v4 URL' do
      expect(project.api_variables.first[:key]).to eq 'CI_API_V4_URL'
      expect(project.api_variables.first[:value]).to include '/api/v4'
    end

4110
    it 'contains a URL variable for every supported API version' do
4111 4112 4113
      # Ensure future API versions have proper variables defined. We're not doing this for v3.
      supported_versions = API::API.versions - ['v3']
      supported_versions = supported_versions.select do |version|
4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125
        API::API.routes.select { |route| route.version == version }.many?
      end

      required_variables = supported_versions.map do |version|
        "CI_API_#{version.upcase}_URL"
      end

      expect(project.api_variables.map { |variable| variable[:key] })
        .to contain_exactly(*required_variables)
    end
  end

4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152
  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
4153
        expect(project.ci_pipelines).to receive(:latest_successful_for_ref).with('foo')
4154 4155 4156 4157 4158 4159

        project.latest_successful_pipeline_for('foo')
      end
    end
  end

4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170
  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

4171 4172 4173 4174 4175 4176 4177 4178 4179 4180
  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)

4181
      expect(project.ci_pipelines).to receive(:latest_successful_for_ref)
4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
        .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
4192 4193

  describe '#after_import' do
4194
    let(:project) { create(:project) }
4195
    let(:import_state) { create(:import_state, project: project) }
4196 4197 4198

    it 'runs the correct hooks' do
      expect(project.repository).to receive(:after_import)
4199
      expect(project.wiki.repository).to receive(:after_import)
4200
      expect(import_state).to receive(:finish)
4201
      expect(project).to receive(:update_project_counter_caches)
4202
      expect(import_state).to receive(:remove_jid)
4203
      expect(project).to receive(:after_create_default_branch)
4204
      expect(project).to receive(:refresh_markdown_cache!)
4205
      expect(InternalId).to receive(:flush_records!).with(project: project)
4206 4207 4208

      project.after_import
    end
4209 4210 4211 4212

    context 'branch protection' do
      let(:project) { create(:project, :repository) }

4213 4214 4215 4216
      before do
        create(:import_state, :started, project: project)
      end

4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249
      it 'does not protect when branch protection is disabled' do
        stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)

        project.after_import

        expect(project.protected_branches).to be_empty
      end

      it "gives developer access to push when branch protection is set to 'developers can push'" do
        stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)

        project.after_import

        expect(project.protected_branches).not_to be_empty
        expect(project.default_branch).to eq(project.protected_branches.first.name)
        expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
      end

      it "gives developer access to merge when branch protection is set to 'developers can merge'" do
        stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)

        project.after_import

        expect(project.protected_branches).not_to be_empty
        expect(project.default_branch).to eq(project.protected_branches.first.name)
        expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
      end

      it 'protects default branch' do
        project.after_import

        expect(project.protected_branches).not_to be_empty
        expect(project.default_branch).to eq(project.protected_branches.first.name)
4250 4251
        expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
        expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
4252 4253
      end
    end
4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284
  end

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

    it 'updates all project counter caches' do
      expect_any_instance_of(Projects::OpenIssuesCountService)
        .to receive(:refresh_cache)
        .and_call_original

      expect_any_instance_of(Projects::OpenMergeRequestsCountService)
        .to receive(:refresh_cache)
        .and_call_original

      project.update_project_counter_caches
    end
  end

  describe '#wiki_repository_exists?' do
    it 'returns true when the wiki repository exists' do
      project = create(:project, :wiki_repo)

      expect(project.wiki_repository_exists?).to eq(true)
    end

    it 'returns false when the wiki repository does not exist' do
      project = create(:project)

      expect(project.wiki_repository_exists?).to eq(false)
    end
  end
4285

4286 4287 4288 4289 4290 4291
  describe '#write_repository_config' do
    set(:project) { create(:project, :repository) }

    it 'writes full path in .git/config when key is missing' do
      project.write_repository_config

4292
      expect(rugged_config['gitlab.fullpath']).to eq project.full_path
4293 4294 4295 4296 4297
    end

    it 'updates full path in .git/config when key is present' do
      project.write_repository_config(gl_full_path: 'old/path')

4298
      expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
4299 4300 4301 4302 4303 4304 4305 4306
    end

    it 'does not raise an error with an empty repository' do
      project = create(:project_empty_repo)

      expect { project.write_repository_config }.not_to raise_error
    end
  end
4307 4308

  describe '#execute_hooks' do
4309
    let(:data) { { ref: 'refs/heads/master', data: 'data' } }
4310
    it 'executes active projects hooks with the specified scope' do
4311 4312 4313 4314
      hook = create(:project_hook, merge_requests_events: false, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([hook])
4315
      project = create(:project, hooks: [hook])
4316 4317 4318

      expect_any_instance_of(ProjectHook).to receive(:async_execute).once

4319
      project.execute_hooks(data, :push_hooks)
4320 4321 4322
    end

    it 'does not execute project hooks that dont match the specified scope' do
4323
      hook = create(:project_hook, merge_requests_events: true, push_events: false)
4324 4325 4326 4327
      project = create(:project, hooks: [hook])

      expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once

4328
      project.execute_hooks(data, :push_hooks)
4329 4330
    end

4331
    it 'does not execute project hooks which are not active' do
4332 4333 4334 4335
      hook = create(:project_hook, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([])
4336 4337 4338 4339
      project = create(:project, hooks: [hook])

      expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once

4340
      project.execute_hooks(data, :push_hooks)
4341 4342 4343
    end

    it 'executes the system hooks with the specified scope' do
4344
      expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(data, :merge_request_hooks)
4345

4346
      project = build(:project)
4347
      project.execute_hooks(data, :merge_request_hooks)
4348
    end
4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361

    it 'executes the system hooks when inside a transaction' do
      allow_any_instance_of(WebHookService).to receive(:execute)

      create(:system_hook, merge_requests_events: true)

      project = build(:project)

      # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
      # but since the entire spec run takes place in a transaction, we never
      # actually get to the `after_commit` hook that queues these jobs.
      expect do
        project.transaction do
4362
          project.execute_hooks(data, :merge_request_hooks)
4363 4364 4365
        end
      end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
    end
4366
  end
4367

4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400
  describe '#has_active_hooks?' do
    set(:project) { create(:project) }

    it { expect(project.has_active_hooks?).to be_falsey }

    it 'returns true when a matching push hook exists' do
      create(:project_hook, push_events: true, project: project)

      expect(project.has_active_hooks?(:merge_request_events)).to be_falsey
      expect(project.has_active_hooks?).to be_truthy
    end

    it 'returns true when a matching system hook exists' do
      create(:system_hook, push_events: true)

      expect(project.has_active_hooks?(:merge_request_events)).to be_falsey
      expect(project.has_active_hooks?).to be_truthy
    end
  end

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

    it { expect(project.has_active_services?).to be_falsey }

    it 'returns true when a matching service exists' do
      create(:custom_issue_tracker_service, push_events: true, merge_requests_events: false, project: project)

      expect(project.has_active_services?(:merge_request_hooks)).to be_falsey
      expect(project.has_active_services?).to be_truthy
    end
  end

4401 4402
  describe '#badges' do
    let(:project_group) { create(:group) }
4403
    let(:project) { create(:project, path: 'avatar', namespace: project_group) }
4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416

    before do
      create_list(:project_badge, 2, project: project)
      create(:group_badge, group: project_group)
    end

    it 'returns the project and the project group badges' do
      create(:group_badge, group: create(:group))

      expect(Badge.count).to eq 4
      expect(project.badges.count).to eq 3
    end

4417 4418
    context 'with nested_groups' do
      let(:parent_group) { create(:group) }
4419

4420 4421 4422 4423
      before do
        create_list(:group_badge, 2, group: project_group)
        project_group.update(parent: parent_group)
      end
4424

4425 4426
      it 'returns the project and the project nested groups badges' do
        expect(project.badges.count).to eq 5
4427 4428 4429
      end
    end
  end
4430

4431 4432
  context 'with cross project merge requests' do
    let(:user) { create(:user) }
4433 4434
    let(:target_project) { create(:project, :repository) }
    let(:project) { fork_project(target_project, nil, repository: true) }
4435 4436 4437 4438 4439 4440 4441 4442 4443 4444
    let!(:local_merge_request) do
      create(
        :merge_request,
        target_project: project,
        target_branch: 'target-branch',
        source_project: project,
        source_branch: 'awesome-feature-1',
        allow_collaboration: true
      )
    end
4445 4446 4447 4448
    let!(:merge_request) do
      create(
        :merge_request,
        target_project: target_project,
4449
        target_branch: 'target-branch',
4450 4451
        source_project: project,
        source_branch: 'awesome-feature-1',
4452
        allow_collaboration: true
4453 4454 4455 4456
      )
    end

    before do
4457
      target_project.add_developer(user)
4458 4459
    end

4460 4461 4462 4463
    describe '#merge_requests_allowing_push_to_user' do
      it 'returns open merge requests for which the user has developer access to the target project' do
        expect(project.merge_requests_allowing_push_to_user(user)).to include(merge_request)
      end
4464

4465 4466
      it 'does not include closed merge requests' do
        merge_request.close
4467

4468 4469 4470 4471 4472 4473
        expect(project.merge_requests_allowing_push_to_user(user)).to be_empty
      end

      it 'does not include merge requests for guest users' do
        guest = create(:user)
        target_project.add_guest(guest)
4474

4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486
        expect(project.merge_requests_allowing_push_to_user(guest)).to be_empty
      end

      it 'does not include the merge request for other users' do
        other_user = create(:user)

        expect(project.merge_requests_allowing_push_to_user(other_user)).to be_empty
      end

      it 'is empty when no user is passed' do
        expect(project.merge_requests_allowing_push_to_user(nil)).to be_empty
      end
4487 4488
    end

4489 4490 4491
    describe '#any_branch_allows_collaboration?' do
      it 'allows access when there are merge requests open allowing collaboration' do
        expect(project.any_branch_allows_collaboration?(user))
4492 4493 4494
          .to be_truthy
      end

4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505
      it 'does not allow access when there are no merge requests open allowing collaboration' do
        merge_request.close!

        expect(project.any_branch_allows_collaboration?(user))
          .to be_falsey
      end
    end

    describe '#branch_allows_collaboration?' do
      it 'allows access if the user can merge the merge request' do
        expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
4506 4507 4508
          .to be_truthy
      end

4509 4510 4511 4512
      it 'does not allow guest users access' do
        guest = create(:user)
        target_project.add_guest(guest)

4513
        expect(project.branch_allows_collaboration?(guest, 'awesome-feature-1'))
4514 4515 4516
          .to be_falsy
      end

4517
      it 'does not allow access to branches for which the merge request was closed' do
4518 4519
        create(:merge_request, :closed,
               target_project: target_project,
4520
               target_branch: 'target-branch',
4521 4522
               source_project: project,
               source_branch: 'rejected-feature-1',
4523
               allow_collaboration: true)
4524

4525
        expect(project.branch_allows_collaboration?(user, 'rejected-feature-1'))
4526 4527 4528
          .to be_falsy
      end

4529
      it 'does not allow access if the user cannot merge the merge request' do
4530
        create(:protected_branch, :maintainers_can_push, project: target_project, name: 'target-branch')
4531

4532
        expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
4533 4534 4535
          .to be_falsy
      end

4536
      context 'when the requeststore is active', :request_store do
4537
        it 'only queries per project across instances' do
4538
          control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') }
4539

4540
          expect { 2.times { described_class.find(project.id).branch_allows_collaboration?(user, 'awesome-feature-1') } }
4541
            .not_to exceed_query_limit(control).with_threshold(2)
4542
        end
4543 4544 4545
      end
    end
  end
Rob Watson's avatar
Rob Watson committed
4546

4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565
  describe '#external_authorization_classification_label' do
    it 'falls back to the default when none is configured' do
      enable_external_authorization_service_check

      expect(build(:project).external_authorization_classification_label)
        .to eq('default_label')
    end

    it 'returns the classification label if it was configured on the project' do
      enable_external_authorization_service_check

      project = build(:project,
                      external_authorization_classification_label: 'hello')

      expect(project.external_authorization_classification_label)
        .to eq('hello')
    end
  end

Rob Watson's avatar
Rob Watson committed
4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609
  describe "#pages_https_only?" do
    subject { build(:project) }

    context "when HTTPS pages are disabled" do
      it { is_expected.not_to be_pages_https_only }
    end

    context "when HTTPS pages are enabled", :https_pages_enabled do
      it { is_expected.to be_pages_https_only }
    end
  end

  describe "#pages_https_only? validation", :https_pages_enabled do
    subject(:project) do
      # set-up dirty object:
      create(:project, pages_https_only: false).tap do |p|
        p.pages_https_only = true
      end
    end

    context "when no domains are associated" do
      it { is_expected.to be_valid }
    end

    context "when domains including keys and certificates are associated" do
      before do
        allow(project)
          .to receive(:pages_domains)
          .and_return([instance_double(PagesDomain, https?: true)])
      end

      it { is_expected.to be_valid }
    end

    context "when domains including no keys or certificates are associated" do
      before do
        allow(project)
          .to receive(:pages_domains)
          .and_return([instance_double(PagesDomain, https?: false)])
      end

      it { is_expected.not_to be_valid }
    end
  end
4610

4611
  describe '#toggle_ci_cd_settings!' do
4612
    it 'toggles the value on #settings' do
4613
      project = create(:project, group_runners_enabled: false)
4614 4615 4616

      expect(project.group_runners_enabled).to be false

4617
      project.toggle_ci_cd_settings!(:group_runners_enabled)
4618 4619 4620 4621

      expect(project.group_runners_enabled).to be true
    end
  end
4622

4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642
  describe '#gitlab_deploy_token' do
    let(:project) { create(:project) }

    subject { project.gitlab_deploy_token }

    context 'when there is a gitlab deploy token associated' do
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }

      it { is_expected.to eq(deploy_token) }
    end

    context 'when there is no a gitlab deploy token associated' do
      it { is_expected.to be_nil }
    end

    context 'when there is a gitlab deploy token associated but is has been revoked' do
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :revoked, projects: [project]) }
      it { is_expected.to be_nil }
    end

4643
    context 'when there is a gitlab deploy token associated but it is expired' do
4644 4645 4646 4647
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) }

      it { is_expected.to be_nil }
    end
4648

4649
    context 'when there is a deploy token associated with a different name' do
4650 4651 4652 4653
      let!(:deploy_token) { create(:deploy_token, projects: [project]) }

      it { is_expected.to be_nil }
    end
4654 4655 4656 4657 4658 4659 4660

    context 'when there is a deploy token associated to a different project' do
      let(:project_2) { create(:project) }
      let!(:deploy_token) { create(:deploy_token, projects: [project_2]) }

      it { is_expected.to be_nil }
    end
4661
  end
Jan Provaznik's avatar
Jan Provaznik committed
4662 4663

  context 'with uploads' do
4664
    it_behaves_like 'model with uploads', true do
Jan Provaznik's avatar
Jan Provaznik committed
4665 4666 4667 4668 4669
      let(:model_object) { create(:project, :with_avatar) }
      let(:upload_attribute) { :avatar }
      let(:uploader_class) { AttachmentUploader }
    end
  end
4670

4671 4672 4673 4674 4675 4676 4677 4678 4679 4680
  context '#commits_by' do
    let(:project) { create(:project, :repository) }
    let(:commits) { project.repository.commits('HEAD', limit: 3).commits }
    let(:commit_shas) { commits.map(&:id) }

    it 'retrieves several commits from the repository by oid' do
      expect(project.commits_by(oids: commit_shas)).to eq commits
    end
  end

4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721
  context '#members_among' do
    let(:users) { create_list(:user, 3) }
    set(:group) { create(:group) }
    set(:project) { create(:project, namespace: group) }

    before do
      project.add_guest(users.first)
      project.group.add_maintainer(users.last)
    end

    context 'when users is an Array' do
      it 'returns project members among the users' do
        expect(project.members_among(users)).to eq([users.first, users.last])
      end

      it 'maintains input order' do
        expect(project.members_among(users.reverse)).to eq([users.last, users.first])
      end

      it 'returns empty array if users is empty' do
        result = project.members_among([])

        expect(result).to be_empty
      end
    end

    context 'when users is a relation' do
      it 'returns project members among the users' do
        result = project.members_among(User.where(id: users.map(&:id)))

        expect(result).to be_a(ActiveRecord::Relation)
        expect(result).to eq([users.first, users.last])
      end

      it 'returns empty relation if users is empty' do
        result = project.members_among(User.none)

        expect(result).to be_a(ActiveRecord::Relation)
        expect(result).to be_empty
      end
    end
4722
  end
4723

4724 4725
  describe "#find_or_initialize_services" do
    subject { build(:project) }
4726

4727 4728 4729
    it 'returns only enabled services' do
      allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
      allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
4730

4731
      services = subject.find_or_initialize_services
4732

4733 4734 4735 4736
      expect(services.count).to eq 1
      expect(services).to include(PushoverService)
    end
  end
4737

4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754
  describe "#find_or_initialize_service" do
    subject { build(:project) }

    it 'avoids N+1 database queries' do
      allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))

      control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count

      allow(Service).to receive(:available_services_names).and_call_original

      expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
    end

    it 'returns nil if service is disabled' do
      allow(subject).to receive(:disabled_services).and_return(%w(prometheus))

      expect(subject.find_or_initialize_service('prometheus')).to be_nil
4755
    end
4756 4757
  end

4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780
  describe '.find_without_deleted' do
    it 'returns nil if the project is about to be removed' do
      project = create(:project, pending_delete: true)

      expect(described_class.find_without_deleted(project.id)).to be_nil
    end

    it 'returns a project when it is not about to be removed' do
      project = create(:project)

      expect(described_class.find_without_deleted(project.id)).to eq(project)
    end
  end

  describe '.for_group' do
    it 'returns the projects for a given group' do
      group = create(:group)
      project = create(:project, namespace: group)

      expect(described_class.for_group(group)).to eq([project])
    end
  end

4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814
  describe '.deployments' do
    subject { project.deployments }

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

    before do
      allow_any_instance_of(Deployment).to receive(:create_ref)
    end

    context 'when there is a deployment record with created status' do
      let(:deployment) { create(:deployment, :created, project: project) }

      it 'does not return the record' do
        is_expected.to be_empty
      end
    end

    context 'when there is a deployment record with running status' do
      let(:deployment) { create(:deployment, :running, project: project) }

      it 'does not return the record' do
        is_expected.to be_empty
      end
    end

    context 'when there is a deployment record with success status' do
      let(:deployment) { create(:deployment, :success, project: project) }

      it 'returns the record' do
        is_expected.to eq([deployment])
      end
    end
  end

4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836
  describe '#snippets_visible?' do
    it 'returns true when a logged in user can read snippets' do
      project = create(:project, :public)
      user = create(:user)

      expect(project.snippets_visible?(user)).to eq(true)
    end

    it 'returns true when an anonymous user can read snippets' do
      project = create(:project, :public)

      expect(project.snippets_visible?).to eq(true)
    end

    it 'returns false when a user can not read snippets' do
      project = create(:project, :private)
      user = create(:user)

      expect(project.snippets_visible?(user)).to eq(false)
    end
  end

4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857
  describe '#all_clusters' do
    let(:project) { create(:project) }
    let(:cluster) { create(:cluster, cluster_type: :project_type, projects: [project]) }

    subject { project.all_clusters }

    it 'returns project level cluster' do
      expect(subject).to eq([cluster])
    end

    context 'project belongs to a group' do
      let(:group_cluster) { create(:cluster, :group) }
      let(:group) { group_cluster.group }
      let(:project) { create(:project, group: group) }

      it 'returns clusters for groups of this project' do
        expect(subject).to contain_exactly(cluster, group_cluster)
      end
    end
  end

4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872
  describe '#object_pool_params' do
    let(:project) { create(:project, :repository, :public) }

    subject { project.object_pool_params }

    context 'when the objects cannot be pooled' do
      let(:project) { create(:project, :repository, :private) }

      it { is_expected.to be_empty }
    end

    context 'when a pool is created' do
      it 'returns that pool repository' do
        expect(subject).not_to be_empty
        expect(subject[:pool_repository]).to be_persisted
4873 4874

        expect(project.reload.pool_repository).to eq(subject[:pool_repository])
4875 4876 4877 4878
      end
    end
  end

4879 4880
  describe '#git_objects_poolable?' do
    subject { project }
4881 4882
    context 'when not using hashed storage' do
      let(:project) { create(:project, :legacy_storage, :public, :repository) }
4883 4884 4885 4886

      it { is_expected.not_to be_git_objects_poolable }
    end

4887 4888
    context 'when the project is not public' do
      let(:project) { create(:project, :private) }
4889

4890 4891
      it { is_expected.not_to be_git_objects_poolable }
    end
4892

4893 4894
    context 'when objects are poolable' do
      let(:project) { create(:project, :repository, :public) }
4895

4896
      it { is_expected.to be_git_objects_poolable }
4897 4898 4899
    end
  end

4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910
  describe '#leave_pool_repository' do
    let(:pool) { create(:pool_repository) }
    let(:project) { create(:project, :repository, pool_repository: pool) }

    it 'removes the membership' do
      project.leave_pool_repository

      expect(pool.member_projects.reload).not_to include(project)
    end
  end

4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979
  describe '#check_personal_projects_limit' do
    context 'when creating a project for a group' do
      it 'does nothing' do
        creator = build(:user)
        project = build(:project, namespace: build(:group), creator: creator)

        allow(creator)
          .to receive(:can_create_project?)
          .and_return(false)

        project.check_personal_projects_limit

        expect(project.errors).to be_empty
      end
    end

    context 'when the user is not allowed to create a personal project' do
      let(:user) { build(:user) }
      let(:project) { build(:project, creator: user) }

      before do
        allow(user)
          .to receive(:can_create_project?)
          .and_return(false)
      end

      context 'when the project limit is zero' do
        it 'adds a validation error' do
          allow(user)
            .to receive(:projects_limit)
            .and_return(0)

          project.check_personal_projects_limit

          expect(project.errors[:limit_reached].first)
            .to match(/Personal project creation is not allowed/)
        end
      end

      context 'when the project limit is greater than zero' do
        it 'adds a validation error' do
          allow(user)
            .to receive(:projects_limit)
            .and_return(5)

          project.check_personal_projects_limit

          expect(project.errors[:limit_reached].first)
            .to match(/Your project limit is 5 projects/)
        end
      end
    end

    context 'when the user is allowed to create personal projects' do
      it 'does nothing' do
        user = build(:user)
        project = build(:project, creator: user)

        allow(user)
          .to receive(:can_create_project?)
          .and_return(true)

        project.check_personal_projects_limit

        expect(project.errors).to be_empty
      end
    end
  end

4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994
  describe '#has_pool_repsitory?' do
    it 'returns false when it does not have a pool repository' do
      subject = create(:project, :repository)

      expect(subject.has_pool_repository?).to be false
    end

    it 'returns true when it has a pool repository' do
      pool    = create(:pool_repository, :ready)
      subject = create(:project, :repository, pool_repository: pool)

      expect(subject.has_pool_repository?).to be true
    end
  end

4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014
  describe '#access_request_approvers_to_be_notified' do
    it 'returns a maximum of ten, active, non_requested maintainers of the project in recent_sign_in descending order' do
      group = create(:group, :public)
      project = create(:project, group: group)

      users = create_list(:user, 12, :with_sign_ins)
      active_maintainers = users.map do |user|
        create(:project_member, :maintainer, user: user)
      end

      create(:project_member, :maintainer, :blocked, project: project)
      create(:project_member, :developer, project: project)
      create(:project_member, :access_request, :maintainer, project: project)

      active_maintainers_in_recent_sign_in_desc_order = project.members_and_requesters.where(id: active_maintainers).order_recent_sign_in.limit(10)

      expect(project.access_request_approvers_to_be_notified).to eq(active_maintainers_in_recent_sign_in_desc_order)
    end
  end

5015
  def rugged_config
5016
    rugged_repo(project.repository).config
5017
  end
5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031

  def create_pipeline(project, status = 'success')
    create(:ci_pipeline, project: project,
                         sha: project.commit.sha,
                         ref: project.default_branch,
                         status: status)
  end

  def create_build(new_pipeline = pipeline, name = 'test')
    create(:ci_build, :success, :artifacts,
           pipeline: new_pipeline,
           status: new_pipeline.status,
           name: name)
  end
gitlabhq's avatar
gitlabhq committed
5032
end