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

G
gitlabhq 已提交
3 4
require 'spec_helper'

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

S
Shinya Maeda 已提交
10
  it_behaves_like 'having unique enum values'
S
Shinya Maeda 已提交
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) }
U
ubudzisz 已提交
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(:unify_circuit_service) }
M
Matt Coleman 已提交
37
    it { is_expected.to have_one(:packagist_service) }
38 39 40 41
    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) }
B
blackst0ne 已提交
42
    it { is_expected.to have_one(:discord_service) }
43 44 45 46 47
    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) }
48
    it { is_expected.to have_one(:hipchat_service) }
49 50 51 52 53 54 55 56 57
    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) }
58
    it { is_expected.to have_one(:youtrack_service) }
59 60 61 62 63
    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) }
64
    it { is_expected.to have_one(:project_repository) }
65
    it { is_expected.to have_one(:container_expiration_policy) }
66 67
    it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
    it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
U
ubudzisz 已提交
68
    it { is_expected.to have_one(:last_event).class_name('Event') }
69
    it { is_expected.to have_one(:forked_from_project).through(:fork_network_member) }
Z
Zeger-Jan van de Weg 已提交
70
    it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
71
    it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') }
72
    it { is_expected.to have_one(:project_setting) }
73
    it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') }
K
Kamil Trzcinski 已提交
74
    it { is_expected.to have_many(:commit_statuses) }
75
    it { is_expected.to have_many(:ci_pipelines) }
76
    it { is_expected.to have_many(:ci_refs) }
77
    it { is_expected.to have_many(:builds) }
78
    it { is_expected.to have_many(:build_trace_section_names)}
79 80 81 82
    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) }
K
Kamil Trzcinski 已提交
83
    it { is_expected.to have_many(:pages_domains) }
84 85
    it { is_expected.to have_many(:labels).class_name('ProjectLabel') }
    it { is_expected.to have_many(:users_star_projects) }
86
    it { is_expected.to have_many(:repository_languages) }
87 88 89 90 91 92 93
    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) }
94 95
    it { is_expected.to have_many(:forked_to_members).class_name('ForkNetworkMember') }
    it { is_expected.to have_many(:forks).through(:forked_to_members) }
J
Jan Provaznik 已提交
96
    it { is_expected.to have_many(:uploads) }
97
    it { is_expected.to have_many(:pipeline_schedules) }
98
    it { is_expected.to have_many(:members_and_requesters) }
M
Matija Čupić 已提交
99
    it { is_expected.to have_many(:clusters) }
100
    it { is_expected.to have_many(:management_clusters).class_name('Clusters::Cluster') }
101
    it { is_expected.to have_many(:kubernetes_namespaces) }
102
    it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') }
103
    it { is_expected.to have_many(:project_badges).class_name('ProjectBadge') }
104
    it { is_expected.to have_many(:lfs_file_locks) }
M
Mayra Cabrera 已提交
105 106
    it { is_expected.to have_many(:project_deploy_tokens) }
    it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) }
107
    it { is_expected.to have_many(:cycle_analytics_stages) }
108
    it { is_expected.to have_many(:external_pull_requests) }
109 110
    it { is_expected.to have_many(:sourced_pipelines) }
    it { is_expected.to have_many(:source_pipelines) }
111 112
    it { is_expected.to have_many(:prometheus_alert_events) }
    it { is_expected.to have_many(:self_managed_prometheus_alert_events) }
113
    it { is_expected.to have_many(:jira_imports) }
114

115 116 117 118 119 120
    it_behaves_like 'model with repository' do
      let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
      let(:stubbed_container) { build_stubbed(:project) }
      let(:expected_full_path) { "#{container.namespace.full_path}/somewhere" }
    end

121 122 123 124
    it 'has an inverse relationship with merge requests' do
      expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
    end

125 126 127 128 129 130 131 132 133 134 135 136 137 138
    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

F
Felipe Artur 已提交
139 140
    context 'after initialized' do
      it "has a project_feature" do
141
        expect(described_class.new.project_feature).to be_present
142 143 144
      end
    end

145
    context 'when creating a new project' do
146
      let_it_be(:project) { create(:project) }
147

148
      it 'automatically creates a CI/CD settings row' do
149 150 151
        expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting)
        expect(project.ci_cd_settings).to be_persisted
      end
152

153 154 155 156 157
      it 'automatically creates a container expiration policy row' do
        expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy)
        expect(project.container_expiration_policy).to be_persisted
      end

158 159 160 161 162 163 164 165 166 167
      it 'does not create another container expiration policy if there is already one' do
        project = build(:project)

        expect do
          container_expiration_policy = create(:container_expiration_policy, project: project)

          expect(project.container_expiration_policy).to eq(container_expiration_policy)
        end.to change { ContainerExpirationPolicy.count }.by(1)
      end

168 169 170 171
      it 'automatically creates a Pages metadata row' do
        expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum)
        expect(project.pages_metadatum).to be_persisted
      end
172 173 174 175 176

      it 'automatically creates a project setting row' do
        expect(project.project_setting).to be_an_instance_of(ProjectSetting)
        expect(project.project_setting).to be_persisted
      end
177 178
    end

179 180 181 182
    context 'updating cd_cd_settings' do
      it 'does not raise an error' do
        project = create(:project)

J
James Lopez 已提交
183
        expect { project.update(ci_cd_settings: nil) }.not_to raise_exception
184 185 186
      end
    end

187
    describe '#members & #requesters' do
188
      let(:project) { create(:project, :public) }
189 190
      let(:requester) { create(:user) }
      let(:developer) { create(:user) }
191

192 193
      before do
        project.request_access(requester)
194
        project.add_developer(developer)
195 196
      end

197 198
      it_behaves_like 'members and requesters associations' do
        let(:namespace) { project }
199 200
      end
    end
201

202
    describe 'ci_pipelines association' do
203 204
      it 'returns only pipelines from ci_sources' do
        expect(Ci::Pipeline).to receive(:ci_sources).and_call_original
205

206
        subject.ci_pipelines
207 208
      end
    end
G
gitlabhq 已提交
209 210
  end

211 212 213 214 215 216 217 218
  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) }
219 220
  end

221
  describe 'validation' do
222
    let!(:project) { create(:project) }
223

224 225
    it { is_expected.to validate_presence_of(:name) }
    it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
226
    it { is_expected.to validate_length_of(:name).is_at_most(255) }
227
    it { is_expected.to validate_presence_of(:path) }
228 229
    it { is_expected.to validate_length_of(:path).is_at_most(255) }
    it { is_expected.to validate_length_of(:description).is_at_most(2000) }
230 231 232
    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) }
233
    it { is_expected.not_to allow_value('/test/foo').for(:ci_config_path) }
234 235
    it { is_expected.to validate_presence_of(:creator) }
    it { is_expected.to validate_presence_of(:namespace) }
236
    it { is_expected.to validate_presence_of(:repository_storage) }
237
    it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
238

239 240 241 242 243
    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)
244
        .with_message('needs to be between 10 minutes and 1 month')
245 246
    end

247
    it 'does not allow new projects beyond user limits' do
248
      project2 = build(:project)
249 250 251 252 253 254 255

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

256
      expect(project2).not_to be_valid
257
    end
258

259 260 261 262 263 264 265
    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

266 267
    describe 'wiki path conflict' do
      context "when the new path has been used by the wiki of other Project" do
268
        it 'has an error on the name attribute' do
269
          new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
270 271

          expect(new_project).not_to be_valid
272
          expect(new_project.errors[:name].first).to eq(_('has already been taken'))
273 274 275 276
        end
      end

      context "when the new wiki path has been used by the path of other Project" do
277
        it 'has an error on the name attribute' do
278 279
          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
280 281

          expect(new_project).not_to be_valid
282
          expect(new_project.errors[:name].first).to eq(_('has already been taken'))
283 284 285
        end
      end
    end
286

287
    context 'repository storages inclusion' do
288
      let(:project2) { build(:project, repository_storage: 'missing') }
289 290

      before do
291
        storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } }
292 293 294
        allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
      end

295
      it "does not allow repository storages that don't match a label in the configuration" do
296 297 298 299
        expect(project2).not_to be_valid
        expect(project2.errors[:repository_storage].first).to match(/is not included in the list/)
      end
    end
300

301 302 303 304 305 306
    it 'validates presence of project_feature' do
      project = build(:project, project_feature: nil)

      expect(project).not_to be_valid
    end

307 308 309
    describe 'import_url' do
      it 'does not allow an invalid URI as import_url' do
        project = build(:project, import_url: 'invalid://')
J
James Lopez 已提交
310

311 312
        expect(project).not_to be_valid
      end
313

314 315 316
      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'
317

318 319
        expect(project).to be_valid
      end
320

321 322
      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')
323

324 325
        expect(project).not_to be_valid
      end
J
James Lopez 已提交
326

327 328
      it 'does allow a valid URI as import_url' do
        project = build(:project, import_url: 'http://gitlab.com/project.git')
J
James Lopez 已提交
329

330 331
        expect(project).to be_valid
      end
332

333 334
      it 'allows an empty URI' do
        project = build(:project, import_url: '')
335

336 337
        expect(project).to be_valid
      end
338

339 340
      it 'does not produce import data on an empty URI' do
        project = build(:project, import_url: '')
341

342 343
        expect(project.import_data).to be_nil
      end
344

345 346
      it 'does not produce import data on an invalid URI' do
        project = build(:project, import_url: 'test://')
347

348 349
        expect(project.import_data).to be_nil
      end
350

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

354 355 356
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
      end
357

358 359 360 361 362 363 364
      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

365 366
      it "does not allow import_url with invalid ports for new projects" do
        project = build(:project, import_url: 'http://github.com:25/t.git')
D
Douwe Maan 已提交
367

368 369 370
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 80, 443')
      end
D
Douwe Maan 已提交
371

372 373 374
      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'
D
Douwe Maan 已提交
375

376 377 378
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
      end
D
Douwe Maan 已提交
379

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

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
        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
401 402
    end

403 404
    describe 'project pending deletion' do
      let!(:project_pending_deletion) do
405
        create(:project,
406 407 408
               pending_delete: true)
      end
      let(:new_project) do
409
        build(:project,
410 411 412 413 414 415 416 417 418
              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
419
        expect(new_project.errors.full_messages.first).to eq(_('The project is still being deleted. Please try again later.'))
420 421
      end
    end
422 423 424

    describe 'path validation' do
      it 'allows paths reserved on the root namespace' do
425
        project = build(:project, path: 'api')
426 427 428 429 430

        expect(project).to be_valid
      end

      it 'rejects paths reserved on another level' do
431
        project = build(:project, path: 'tree')
432 433 434

        expect(project).not_to be_valid
      end
435 436 437

      it 'rejects nested paths' do
        parent = create(:group, :nested, path: 'environments')
438
        project = build(:project, path: 'folders', namespace: parent)
439 440 441

        expect(project).not_to be_valid
      end
442 443 444

      it 'allows a reserved group name' do
        parent = create(:group)
445
        project = build(:project, path: 'avatar', namespace: parent)
446 447 448

        expect(project).to be_valid
      end
449 450 451 452 453 454

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

        expect(project).to be_valid
      end
455
    end
G
gitlabhq 已提交
456
  end
457

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
  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

475
      it 'returns .external pipelines' do
476 477 478 479 480 481
        expect(project.all_pipelines).to all(have_attributes(source: 'external'))
        expect(project.all_pipelines.size).to eq(1)
      end
    end
  end

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
  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

499
      it 'returns .external pipelines' do
500 501 502 503 504 505
        expect(project.ci_pipelines).to all(have_attributes(source: 'external'))
        expect(project.ci_pipelines.size).to eq(1)
      end
    end
  end

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
  describe '#autoclose_referenced_issues' do
    context 'when DB entry is nil' do
      let(:project) { create(:project, autoclose_referenced_issues: nil) }

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

    context 'when DB entry is true' do
      let(:project) { create(:project, autoclose_referenced_issues: true) }

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

    context 'when DB entry is false' do
      let(:project) { create(:project, autoclose_referenced_issues: false) }

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

K
Kamil Trzcinski 已提交
532
  describe 'project token' do
533
    it 'sets an random token if none provided' do
534
      project = FactoryBot.create(:project, runners_token: '')
K
Kamil Trzcinski 已提交
535
      expect(project.runners_token).not_to eq('')
K
Kamil Trzcinski 已提交
536 537
    end

U
ubudzisz 已提交
538
    it 'does not set an random token if one provided' do
539
      project = FactoryBot.create(:project, runners_token: 'my-token')
K
Kamil Trzcinski 已提交
540
      expect(project.runners_token).to eq('my-token')
K
Kamil Trzcinski 已提交
541 542
    end
  end
G
gitlabhq 已提交
543

544
  describe 'Respond to' do
545 546 547 548
    it { is_expected.to respond_to(:url_to_repo) }
    it { is_expected.to respond_to(:execute_hooks) }
    it { is_expected.to respond_to(:owner) }
    it { is_expected.to respond_to(:path_with_namespace) }
549
    it { is_expected.to respond_to(:full_path) }
G
gitlabhq 已提交
550 551
  end

552
  describe 'delegation' do
553
    [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_user, :add_users].each do |method|
554 555 556 557 558
      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) }
559
    it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
560
    it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
561 562
  end

563 564 565 566 567 568 569
  describe 'reference methods' do
    let_it_be(:owner)     { create(:user, name: 'Gitlab') }
    let_it_be(:namespace) { create(:namespace, name: 'Sample namespace', path: 'sample-namespace', owner: owner) }
    let_it_be(:project)   { create(:project, name: 'Sample project', path: 'sample-project', namespace: namespace) }
    let_it_be(:group)     { create(:group, name: 'Group', path: 'sample-group') }
    let_it_be(:another_project) { create(:project, namespace: namespace) }
    let_it_be(:another_namespace_project) { create(:project, name: 'another-project') }
570

571 572 573 574
    describe '#to_reference' do
      it 'returns the path with reference_postfix' do
        expect(project.to_reference).to eq("#{project.full_path}>")
      end
575

576 577
      it 'returns the path with reference_postfix when arg is self' do
        expect(project.to_reference(project)).to eq("#{project.full_path}>")
578 579
      end

580 581
      it 'returns the full_path with reference_postfix when full' do
        expect(project.to_reference(full: true)).to eq("#{project.full_path}>")
582 583
      end

584 585
      it 'returns the full_path with reference_postfix when cross-project' do
        expect(project.to_reference(build_stubbed(:project))).to eq("#{project.full_path}>")
586 587 588
      end
    end

589 590 591 592 593
    describe '#to_reference_base' do
      context 'when nil argument' do
        it 'returns nil' do
          expect(project.to_reference_base).to be_nil
        end
594 595
      end

596 597 598
      context 'when full is true' do
        it 'returns complete path to the project', :aggregate_failures do
          be_full_path = eq('sample-namespace/sample-project')
599

600 601 602 603
          expect(project.to_reference_base(full: true)).to be_full_path
          expect(project.to_reference_base(project, full: true)).to be_full_path
          expect(project.to_reference_base(group, full: true)).to be_full_path
        end
604
      end
605

606 607 608 609 610
      context 'when same project argument' do
        it 'returns nil' do
          expect(project.to_reference_base(project)).to be_nil
        end
      end
611

612 613 614 615
      context 'when cross namespace project argument' do
        it 'returns complete path to the project' do
          expect(project.to_reference_base(another_namespace_project)).to eq 'sample-namespace/sample-project'
        end
616 617
      end

618
      context 'when same namespace / cross-project argument' do
619
        it 'returns path to the project' do
620
          expect(project.to_reference_base(another_project)).to eq 'sample-project'
621 622 623
        end
      end

624 625 626 627
      context 'when different namespace / cross-project argument with same owner' do
        let(:another_namespace_same_owner) { create(:namespace, path: 'another-namespace', owner: owner) }
        let(:another_project_same_owner)   { create(:project, path: 'another-project', namespace: another_namespace_same_owner) }

628
        it 'returns full path to the project' do
629
          expect(project.to_reference_base(another_project_same_owner)).to eq 'sample-namespace/sample-project'
630
        end
631
      end
632

633 634 635 636 637 638
      context 'when argument is a namespace' do
        context 'with same project path' do
          it 'returns path to the project' do
            expect(project.to_reference_base(namespace)).to eq 'sample-project'
          end
        end
639

640 641 642 643 644
        context 'with different project path' do
          it 'returns full path to the project' do
            expect(project.to_reference_base(group)).to eq 'sample-namespace/sample-project'
          end
        end
645 646 647
      end
    end

648 649 650 651 652
    describe '#to_human_reference' do
      context 'when nil argument' do
        it 'returns nil' do
          expect(project.to_human_reference).to be_nil
        end
653 654
      end

655 656 657 658
      context 'when same project argument' do
        it 'returns nil' do
          expect(project.to_human_reference(project)).to be_nil
        end
659 660
      end

661 662 663 664 665
      context 'when cross namespace project argument' do
        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
666

667 668 669 670
      context 'when same namespace / cross-project argument' do
        it 'returns name of the project' do
          expect(project.to_human_reference(another_project)).to eq 'Sample project'
        end
671
      end
672 673 674
    end
  end

V
Valery Sizov 已提交
675
  describe '#merge_method' do
676 677 678 679 680 681 682
    using RSpec::Parameterized::TableSyntax

    where(:ff, :rebase, :method) do
      true  | true  | :ff
      true  | false | :ff
      false | true  | :rebase_merge
      false | false | :merge
V
Valery Sizov 已提交
683 684
    end

685 686 687 688 689 690
    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) }
V
Valery Sizov 已提交
691 692 693
    end
  end

694
  it 'returns valid url to repo' do
695
    project = described_class.new(path: 'somewhere')
696
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
G
gitlabhq 已提交
697 698
  end

I
Imre Farkas 已提交
699 700
  describe "#readme_url" do
    context 'with a non-existing repository' do
701
      let(:project) { create(:project) }
I
Imre Farkas 已提交
702

703
      it 'returns nil' do
I
Imre Farkas 已提交
704 705 706 707 708 709
        expect(project.readme_url).to be_nil
      end
    end

    context 'with an existing repository' do
      context 'when no README exists' do
710
        let(:project) { create(:project, :empty_repo) }
I
Imre Farkas 已提交
711

712
        it 'returns nil' do
I
Imre Farkas 已提交
713 714 715 716 717
          expect(project.readme_url).to be_nil
        end
      end

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

I
Imre Farkas 已提交
720
        it 'returns the README' do
721
          expect(project.readme_url).to eq("#{project.web_url}/-/blob/master/README.md")
I
Imre Farkas 已提交
722 723 724 725 726
        end
      end
    end
  end

J
Jan Provaznik 已提交
727
  describe "#new_issuable_address" do
728
    let(:project) { create(:project, path: "somewhere") }
729 730
    let(:user) { create(:user) }

731 732 733 734 735 736
    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
737
        address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-issue@gl.ab"
738

J
Jan Provaznik 已提交
739 740 741 742
        expect(project.new_issuable_address(user, 'issue')).to eq(address)
      end

      it 'returns the address to create a new merge request' do
743
        address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-merge-request@gl.ab"
J
Jan Provaznik 已提交
744 745

        expect(project.new_issuable_address(user, 'merge_request')).to eq(address)
746
      end
747 748 749 750

      it 'returns nil with invalid address type' do
        expect(project.new_issuable_address(user, 'invalid_param')).to be_nil
      end
751 752 753 754 755 756
    end

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

758
      it 'returns nil' do
J
Jan Provaznik 已提交
759 760 761 762 763
        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
764
      end
765 766 767
    end
  end

768
  describe 'last_activity methods' do
S
Stan Hu 已提交
769 770
    let(:timestamp) { 2.hours.ago }
    # last_activity_at gets set to created_at upon creation
771
    let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
G
gitlabhq 已提交
772

773
    describe 'last_activity' do
774
      it 'alias last_activity to last_event' do
775
        last_event = create(:event, :closed, project: project)
776

777
        expect(project.last_activity).to eq(last_event)
778
      end
G
gitlabhq 已提交
779 780
    end

781 782
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
783
        new_event = create(:event, :closed, project: project, created_at: Time.now)
784

S
Stan Hu 已提交
785
        project.reload
786
        expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
787
      end
788

789
      it 'returns the project\'s last update date if it has no events' do
790
        expect(project.last_activity_date).to eq(project.updated_at)
791
      end
792 793

      it 'returns the most recent timestamp' do
L
Lin Jen-Shin 已提交
794 795 796
        project.update(updated_at: nil,
                       last_activity_at: timestamp,
                       last_repository_updated_at: timestamp - 1.hour)
797

798
        expect(project.last_activity_date).to be_like_time(timestamp)
799

L
Lin Jen-Shin 已提交
800 801 802
        project.update(updated_at: timestamp,
                       last_activity_at: timestamp - 1.hour,
                       last_repository_updated_at: nil)
803

804
        expect(project.last_activity_date).to be_like_time(timestamp)
805
      end
806 807
    end
  end
808

809
  describe '#get_issue' do
810
    let(:project) { create(:project) }
S
Stan Hu 已提交
811
    let!(:issue)  { create(:issue, project: project) }
812 813 814
    let(:user)    { create(:user) }

    before do
815
      project.add_developer(user)
816
    end
817 818 819

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

S
Stan Hu 已提交
823 824 825 826
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

827
      it 'returns nil when no issue found' do
828
        expect(project.get_issue(non_existing_record_id, user)).to be_nil
829 830 831 832 833
      end

      it "returns nil when user doesn't have access" do
        user = create(:user)
        expect(project.get_issue(issue.iid, user)).to eq nil
834 835 836 837
      end
    end

    context 'with external issues tracker' do
838
      let!(:internal_issue) { create(:issue, project: project) }
839

840
      before do
841
        allow(project).to receive(:external_issue_tracker).and_return(true)
842 843
      end

844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
      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
881 882 883 884 885
      end
    end
  end

  describe '#issue_exists?' do
886
    let(:project) { create(:project) }
887 888 889 890 891 892 893 894 895 896 897 898

    it 'is truthy when issue exists' do
      expect(project).to receive(:get_issue).and_return(double)
      expect(project.issue_exists?(1)).to be_truthy
    end

    it 'is falsey when issue does not exist' do
      expect(project).to receive(:get_issue).and_return(nil)
      expect(project.issue_exists?(1)).to be_falsey
    end
  end

L
Lin Jen-Shin 已提交
899
  describe '#to_param' do
900 901
    context 'with namespace' do
      before do
902
        @group = create(:group, name: 'gitlab')
903
        @project = create(:project, name: 'gitlabhq', namespace: @group)
904 905
      end

V
Vinnie Okada 已提交
906
      it { expect(@project.to_param).to eq('gitlabhq') }
907
    end
908 909 910

    context 'with invalid path' do
      it 'returns previous path to keep project suitable for use in URLs when persisted' do
911
        project = create(:project, path: 'gitlab')
912 913 914 915 916 917 918
        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
919
        project = build(:project, path: 'gitlab')
920 921 922 923 924 925
        project.path = 'foo&bar'

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

L
Lin Jen-Shin 已提交
928
  describe '#default_issues_tracker?' do
929
    it "is true if used internal tracker" do
930
      project = build(:project)
931

932
      expect(project.default_issues_tracker?).to be_truthy
933 934
    end

935
    it "is false if used other tracker" do
936 937 938 939
      # NOTE: The current nature of this factory requires persistence
      project = create(:redmine_project)

      expect(project.default_issues_tracker?).to be_falsey
940 941 942
    end
  end

L
Lin Jen-Shin 已提交
943
  describe '#external_issue_tracker' do
944
    let(:project) { create(:project) }
945 946 947
    let(:ext_project) { create(:redmine_project) }

    context 'on existing projects with no value for has_external_issue_tracker' do
948
      before do
949 950 951 952 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
        project.update_column(:has_external_issue_tracker, nil)
        ext_project.update_column(:has_external_issue_tracker, nil)
      end

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

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

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

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

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

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

L
Lin Jen-Shin 已提交
978
  describe '#cache_has_external_issue_tracker' do
979
    let(:project) { create(:project, has_external_issue_tracker: nil) }
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997

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

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

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

      expect do
        project.cache_has_external_issue_tracker
      end.to change { project.has_external_issue_tracker}.to(false)
    end
T
Toon Claes 已提交
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

    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
1036 1037
  end

1038
  describe '#has_wiki?' do
1039 1040 1041
    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) }
1042 1043 1044 1045 1046 1047 1048 1049

    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

1050
  describe '#external_wiki' do
1051
    let(:project) { create(:project) }
1052

1053 1054 1055 1056 1057
    context 'with an active external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: true)
        project.external_wiki
      end
1058

1059 1060 1061
      it 'sets :has_external_wiki as true' do
        expect(project.has_external_wiki).to be(true)
      end
1062

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

1066 1067 1068 1069
        project.services.external_wikis.first.destroy

        expect(project.has_external_wiki).to be(false)
      end
1070 1071
    end

1072 1073 1074 1075
    context 'with an inactive external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: false)
      end
1076

1077 1078 1079
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end
1080 1081
    end

1082 1083 1084 1085
    context 'with no external wiki' do
      before do
        project.external_wiki
      end
1086

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097
      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
1098 1099 1100
    end
  end

1101 1102
  describe '#star_count' do
    it 'counts stars from multiple users' do
1103 1104
      user1 = create(:user)
      user2 = create(:user)
1105
      project = create(:project, :public)
C
Ciro Santilli 已提交
1106 1107

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

C
Ciro Santilli 已提交
1109
      user1.toggle_star(project)
1110 1111
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
1112
      user2.toggle_star(project)
1113 1114 1115
      project.reload
      expect(project.reload.star_count).to eq(2)

C
Ciro Santilli 已提交
1116
      user1.toggle_star(project)
1117 1118 1119
      project.reload
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
1120
      user2.toggle_star(project)
1121 1122 1123 1124
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

1125
    it 'counts stars on the right project' do
1126
      user = create(:user)
1127 1128
      project1 = create(:project, :public)
      project2 = create(:project, :public)
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155

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

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

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

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

      user.toggle_star(project2)
      project1.reload
      project2.reload
      expect(project1.star_count).to eq(0)
      expect(project2.star_count).to eq(0)
C
Ciro Santilli 已提交
1156 1157
    end
  end
1158

L
Lin Jen-Shin 已提交
1159
  describe '#avatar_type' do
1160
    let(:project) { create(:project) }
1161

1162
    it 'is true if avatar is image' do
1163
      project.update_attribute(:avatar, 'uploads/avatar.png')
1164
      expect(project.avatar_type).to be_truthy
1165 1166
    end

1167
    it 'is false if avatar is html page' do
1168
      project.update_attribute(:avatar, 'uploads/avatar.html')
1169
      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'])
1170 1171
    end
  end
S
sue445 已提交
1172

L
Lin Jen-Shin 已提交
1173
  describe '#avatar_url' do
S
sue445 已提交
1174 1175
    subject { project.avatar_url }

1176
    let(:project) { create(:project) }
S
sue445 已提交
1177

1178
    context 'when avatar file is uploaded' do
T
Tim Zallmann 已提交
1179
      let(:project) { create(:project, :public, :with_avatar) }
S
sue445 已提交
1180

1181
      it 'shows correct url' do
1182 1183
        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)
1184
      end
S
sue445 已提交
1185 1186
    end

1187
    context 'when avatar file in git' do
S
sue445 已提交
1188 1189 1190 1191
      before do
        allow(project).to receive(:avatar_in_git) { true }
      end

1192
      let(:avatar_path) { "/#{project.full_path}/-/avatar" }
S
sue445 已提交
1193

1194
      it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
S
sue445 已提交
1195
    end
1196 1197

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

1200
      it { is_expected.to eq nil }
1201
    end
S
sue445 已提交
1202
  end
1203

1204
  describe '#pipeline_for' do
1205
    let(:project) { create(:project, :repository) }
K
Kamil Trzcinski 已提交
1206

1207 1208
    shared_examples 'giving the correct pipeline' do
      it { is_expected.to eq(pipeline) }
K
Kamil Trzcinski 已提交
1209

1210
      context 'return latest' do
1211
        let!(:pipeline2) { create_pipeline(project) }
K
Kamil Trzcinski 已提交
1212

1213
        it { is_expected.to eq(pipeline2) }
K
Kamil Trzcinski 已提交
1214
      end
1215 1216
    end

M
Matija Čupić 已提交
1217 1218 1219 1220 1221
    context 'with a matching pipeline' do
      let!(:pipeline) { create_pipeline(project) }

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

M
Matija Čupić 已提交
1223
        it_behaves_like 'giving the correct pipeline'
1224

M
Matija Čupić 已提交
1225 1226
        context 'with supplied id' do
          let!(:other_pipeline) { create_pipeline(project) }
1227

M
Matija Čupić 已提交
1228
          subject { project.pipeline_for('master', pipeline.sha, other_pipeline.id) }
1229

M
Matija Čupić 已提交
1230 1231 1232 1233 1234 1235 1236 1237
          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'
1238
      end
1239 1240
    end

M
Matija Čupić 已提交
1241
    context 'when there is no matching pipeline' do
1242 1243
      subject { project.pipeline_for('master') }

M
Matija Čupić 已提交
1244
      it { is_expected.to be_nil }
1245
    end
1246
  end
1247

1248 1249 1250 1251 1252
  describe '#pipelines_for' do
    let(:project) { create(:project, :repository) }
    let!(:pipeline) { create_pipeline(project) }
    let!(:other_pipeline) { create_pipeline(project) }

M
Matija Čupić 已提交
1253
    subject { project.pipelines_for(project.default_branch, project.commit.sha) }
1254

M
Matija Čupić 已提交
1255
    it { is_expected.to contain_exactly(pipeline, other_pipeline) }
1256 1257
  end

L
Lin Jen-Shin 已提交
1258
  describe '#builds_enabled' do
1259
    let(:project) { create(:project) }
1260

1261 1262 1263
    subject { project.builds_enabled }

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

1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
  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

1278
  describe '.with_shared_runners' do
1279
    subject { described_class.with_shared_runners }
1280 1281

    context 'when shared runners are enabled for project' do
1282
      let!(:project) { create(:project, shared_runners_enabled: true) }
1283 1284 1285 1286 1287 1288 1289

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

    context 'when shared runners are disabled for project' do
1290
      let!(:project) { create(:project, shared_runners_enabled: false) }
1291 1292 1293 1294 1295 1296 1297

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

1298
  describe '.cached_count', :use_clean_rails_memory_store_caching do
1299
    let(:group)     { create(:group, :public) }
1300 1301
    let!(:project1) { create(:project, :public, group: group) }
    let!(:project2) { create(:project, :public, group: group) }
1302 1303

    it 'returns total project count' do
1304
      expect(described_class).to receive(:count).once.and_call_original
1305 1306

      3.times do
1307
        expect(described_class.cached_count).to eq(2)
1308 1309 1310 1311
      end
    end
  end

Y
Yorick Peterse 已提交
1312
  describe '.trending' do
F
Felipe Artur 已提交
1313
    let(:group)    { create(:group, :public) }
1314 1315
    let(:project1) { create(:project, :public, :repository, group: group) }
    let(:project2) { create(:project, :public, :repository, group: group) }
Y
Yorick Peterse 已提交
1316 1317

    before do
1318
      create_list(:note_on_commit, 2, project: project1)
Y
Yorick Peterse 已提交
1319 1320 1321

      create(:note_on_commit, project: project2)

Y
Yorick Peterse 已提交
1322
      TrendingProject.refresh!
Y
Yorick Peterse 已提交
1323 1324
    end

Y
Yorick Peterse 已提交
1325
    subject { described_class.trending.to_a }
Y
Yorick Peterse 已提交
1326

Y
Yorick Peterse 已提交
1327 1328
    it 'sorts projects by the amount of notes in descending order' do
      expect(subject).to eq([project1, project2])
Y
Yorick Peterse 已提交
1329
    end
1330 1331

    it 'does not take system notes into account' do
1332
      create_list(:note_on_commit, 10, project: project2, system: true)
1333 1334 1335

      expect(described_class.trending.to_a).to eq([project1, project2])
    end
Y
Yorick Peterse 已提交
1336
  end
Y
Yorick Peterse 已提交
1337

T
Toon Claes 已提交
1338 1339 1340 1341
  describe '.starred_by' do
    it 'returns only projects starred by the given user' do
      user1 = create(:user)
      user2 = create(:user)
1342 1343 1344
      project1 = create(:project)
      project2 = create(:project)
      create(:project)
T
Toon Claes 已提交
1345 1346 1347
      user1.toggle_star(project1)
      user2.toggle_star(project2)

1348
      expect(described_class.starred_by(user1)).to contain_exactly(project1)
T
Toon Claes 已提交
1349 1350 1351
    end
  end

1352 1353 1354 1355 1356 1357 1358 1359
  describe '.with_limit' do
    it 'limits the number of projects returned' do
      create_list(:project, 3)

      expect(described_class.with_limit(1).count).to eq(1)
    end
  end

Y
Yorick Peterse 已提交
1360
  describe '.visible_to_user' do
1361
    let!(:project) { create(:project, :private) }
Y
Yorick Peterse 已提交
1362 1363 1364 1365 1366 1367
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
1368
        project.add_user(user, Gitlab::Access::MAINTAINER)
Y
Yorick Peterse 已提交
1369 1370 1371 1372 1373 1374 1375 1376 1377
      end

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

    describe 'when a user does not have access to any projects' do
      it { is_expected.to eq([]) }
    end
  end
K
Kamil Trzcinski 已提交
1378

1379 1380 1381 1382 1383 1384 1385 1386 1387 1388
  describe '.with_service' do
    before do
      create_list(:prometheus_project, 2)
    end

    it 'avoid n + 1' do
      expect { described_class.with_service(:prometheus_service).map(&:prometheus_service) }.not_to exceed_query_limit(1)
    end
  end

1389
  context 'repository storage by default' do
1390
    let(:project) { build(:project) }
1391

1392
    it 'picks storage from ApplicationSetting' do
1393 1394 1395 1396
      expect_next_instance_of(ApplicationSetting) do |instance|
        expect(instance).to receive(:pick_repository_storage).and_return('picked')
      end
      expect(described_class).to receive(:pick_repository_storage).and_call_original
1397 1398 1399

      expect(project.repository_storage).to eq('picked')
    end
1400 1401
  end

K
Kamil Trzcinski 已提交
1402
  context 'shared runners by default' do
1403
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
1404 1405 1406 1407

    subject { project.shared_runners_enabled }

    context 'are enabled' do
1408 1409 1410
      before do
        stub_application_setting(shared_runners_enabled: true)
      end
K
Kamil Trzcinski 已提交
1411 1412 1413 1414 1415

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
1416 1417 1418
      before do
        stub_application_setting(shared_runners_enabled: false)
      end
K
Kamil Trzcinski 已提交
1419 1420 1421 1422 1423

      it { is_expected.to be_falsey }
    end
  end

1424
  describe '#any_runners?' do
1425
    context 'shared runners' do
1426
      let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
1427 1428
      let(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
      let(:shared_runner) { create(:ci_runner, :instance) }
K
Kamil Trzcinski 已提交
1429

1430 1431
      context 'for shared runners disabled' do
        let(:shared_runners_enabled) { false }
1432

1433 1434 1435
        it 'has no runners available' do
          expect(project.any_runners?).to be_falsey
        end
1436

1437
        it 'has a specific runner' do
1438
          specific_runner
1439

1440 1441 1442 1443 1444
          expect(project.any_runners?).to be_truthy
        end

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

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

1449
        it 'checks the presence of specific runner' do
1450
          specific_runner
1451

1452 1453
          expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
        end
1454 1455

        it 'returns false if match cannot be found' do
1456
          specific_runner
1457

1458 1459
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1460
      end
1461

1462 1463 1464 1465 1466
      context 'for shared runners enabled' do
        let(:shared_runners_enabled) { true }

        it 'has a shared runner' do
          shared_runner
1467

1468 1469 1470 1471 1472
          expect(project.any_runners?).to be_truthy
        end

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

1474 1475
          expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
        end
1476 1477 1478

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

1480 1481
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1482 1483
      end
    end
1484

1485
    context 'group runners' do
1486 1487
      let(:project) { create(:project, group_runners_enabled: group_runners_enabled) }
      let(:group) { create(:group, projects: [project]) }
1488
      let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
1489 1490 1491

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

1493 1494 1495 1496 1497 1498
        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
1499

1500 1501
          expect(project.any_runners?).to be_falsey
        end
K
Kamil Trzcinski 已提交
1502 1503
      end

1504 1505 1506 1507 1508
      context 'for group runners enabled' do
        let(:group_runners_enabled) { true }

        it 'has a group runner' do
          group_runner
1509

1510 1511 1512 1513 1514
          expect(project.any_runners?).to be_truthy
        end

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

1516 1517
          expect(project.any_runners? { |runner| runner == group_runner }).to be_truthy
        end
1518 1519 1520

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

1522 1523
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1524 1525 1526
      end
    end
  end
1527

1528
  describe '#shared_runners' do
1529
    let!(:runner) { create(:ci_runner, :instance) }
1530 1531 1532 1533

    subject { project.shared_runners }

    context 'when shared runners are enabled for project' do
1534
      let!(:project) { create(:project, shared_runners_enabled: true) }
1535 1536 1537 1538 1539 1540 1541

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

    context 'when shared runners are disabled for project' do
1542
      let!(:project) { create(:project, shared_runners_enabled: false) }
1543 1544 1545 1546 1547 1548 1549

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

1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
  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
1560 1561
      using RSpec::Parameterized::TableSyntax

1562 1563 1564 1565 1566
      before do
        stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
      end

      it { is_expected.to eq(Gitlab::VisibilityLevel::INTERNAL) }
1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581

      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
1582 1583 1584
    end
  end

1585
  describe '#visibility_level_allowed?' do
1586
    let(:project) { create(:project, :internal) }
1587 1588 1589 1590 1591 1592 1593 1594

    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
1595
      let(:project)        { create(:project, :internal) }
1596
      let(:forked_project) { fork_project(project) }
1597 1598 1599 1600 1601

      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
1602
  end
1603

1604
  describe '#pages_deployed?' do
1605
    let(:project) { create(:project) }
1606 1607 1608 1609

    subject { project.pages_deployed? }

    context 'if public folder does exist' do
1610 1611 1612
      before do
        allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
      end
1613 1614 1615 1616 1617 1618 1619 1620 1621

      it { is_expected.to be_truthy }
    end

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

1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644
  describe '#default_branch_protected?' do
    using RSpec::Parameterized::TableSyntax

    let_it_be(:project) { create(:project) }

    subject { project.default_branch_protected? }

    where(:default_branch_protection_level, :result) do
      Gitlab::Access::PROTECTION_NONE           | false
      Gitlab::Access::PROTECTION_DEV_CAN_PUSH   | false
      Gitlab::Access::PROTECTION_DEV_CAN_MERGE  | true
      Gitlab::Access::PROTECTION_FULL           | true
    end

    with_them do
      before do
        expect(project.namespace).to receive(:default_branch_protection).and_return(default_branch_protection_level)
      end

      it { is_expected.to eq(result) }
    end
  end

1645
  describe '#pages_url' do
1646 1647
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668
    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") }
1669 1670 1671 1672
    end
  end

  describe '#pages_group_url' do
1673 1674
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696
    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}") }
1697 1698 1699
    end
  end

1700
  describe '.search' do
1701
    let_it_be(:project) { create(:project, description: 'kitten mittens') }
1702

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

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

1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
    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

1739
    context 'when include_namespace is true' do
1740 1741 1742 1743 1744 1745 1746 1747 1748
      let_it_be(:group) { create(:group) }
      let_it_be(:project) { create(:project, group: group) }

      context 'when feature is enabled' do
        before do
          stub_feature_flags(project_search_by_full_path: true)
        end

        it 'returns projects that match the group path' do
1749
          expect(described_class.search(group.path, include_namespace: true)).to eq([project])
1750 1751 1752
        end

        it 'returns projects that match the full path' do
1753
          expect(described_class.search(project.full_path, include_namespace: true)).to eq([project])
1754 1755 1756 1757 1758 1759 1760 1761 1762
        end
      end

      context 'when feature is disabled' do
        before do
          stub_feature_flags(project_search_by_full_path: false)
        end

        it 'returns no results when searching by group path' do
1763
          expect(described_class.search(group.path, include_namespace: true)).to be_empty
1764 1765 1766
        end

        it 'returns no results when searching by full path' do
1767
          expect(described_class.search(project.full_path, include_namespace: true)).to be_empty
1768 1769 1770 1771
        end
      end
    end

1772
    describe 'with pending_delete project' do
1773
      let(:pending_delete_project) { create(:project, pending_delete: true) }
1774 1775 1776 1777 1778 1779 1780

      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
1781
  end
1782

Y
Yorick Peterse 已提交
1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798
  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

1799
  describe '.eager_load_namespace_and_owner' do
Y
Yorick Peterse 已提交
1800 1801 1802
    it 'eager loads the namespace and namespace owner' do
      create(:project)

1803
      row = described_class.eager_load_namespace_and_owner.first
Y
Yorick Peterse 已提交
1804 1805 1806 1807 1808 1809
      recorder = ActiveRecord::QueryRecorder.new { row.namespace.owner }

      expect(recorder.count).to be_zero
    end
  end

1810
  describe '#expire_caches_before_rename' do
1811
    let(:project) { create(:project, :repository) }
1812 1813 1814 1815
    let(:repo)    { double(:repo, exists?: true) }
    let(:wiki)    { double(:wiki, exists?: true) }

    it 'expires the caches of the repository and wiki' do
1816 1817 1818
      # In EE, there are design repositories as well
      allow(Repository).to receive(:new).and_call_original

1819
      allow(Repository).to receive(:new)
1820
        .with('foo', project, shard: project.repository_storage)
1821
        .and_return(repo)
1822

1823
      allow(Repository).to receive(:new)
1824
        .with('foo.wiki', project, shard: project.repository_storage, repo_type: Gitlab::GlRepository::WIKI)
1825
        .and_return(wiki)
1826

1827 1828
      expect(repo).to receive(:before_delete)
      expect(wiki).to receive(:before_delete)
1829 1830 1831 1832

      project.expire_caches_before_rename('foo')
    end
  end
1833 1834

  describe '.search_by_title' do
1835
    let(:project) { create(:project, name: 'kittens') }
1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848

    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
1849 1850 1851 1852 1853

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

1854 1855
    let(:private_project)  { create(:project, :private, group: private_group) }
    let(:internal_project) { create(:project, :internal, group: internal_group) }
1856 1857 1858 1859 1860 1861 1862 1863 1864

    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
1865

1866
  describe '#track_project_repository' do
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881
    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
1882

1883 1884 1885 1886 1887 1888 1889
      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
1890

1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902
        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
1903 1904
    end

1905 1906
    context 'with projects on legacy storage' do
      let(:project) { create(:project, :repository, :legacy_storage) }
1907

1908 1909
      it_behaves_like 'tracks storage location'
    end
1910

1911 1912
    context 'with projects on hashed storage' do
      let(:project) { create(:project, :repository) }
1913

1914
      it_behaves_like 'tracks storage location'
1915 1916 1917
    end
  end

1918
  describe '#create_repository' do
1919
    let(:project) { create(:project, :repository) }
1920 1921 1922

    context 'using a regular repository' do
      it 'creates the repository' do
1923
        expect(project.repository).to receive(:create_repository)
1924 1925 1926 1927
        expect(project.create_repository).to eq(true)
      end

      it 'adds an error if the repository could not be created' do
1928
        expect(project.repository).to receive(:create_repository) { raise 'Fail in test' }
1929 1930 1931 1932 1933 1934 1935 1936
        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)
1937
        expect(project.repository).not_to receive(:create_repository)
1938 1939 1940 1941 1942

        project.create_repository
      end
    end
  end
1943

1944 1945 1946 1947
  describe '#ensure_repository' do
    let(:project) { create(:project, :repository) }

    it 'creates the repository if it not exist' do
1948
      allow(project).to receive(:repository_exists?).and_return(false)
1949
      expect(project).to receive(:create_repository).with(force: true)
1950 1951 1952 1953 1954

      project.ensure_repository
    end

    it 'does not create the repository if it exists' do
1955
      allow(project).to receive(:repository_exists?).and_return(true)
1956 1957 1958 1959 1960

      expect(project).not_to receive(:create_repository)

      project.ensure_repository
    end
1961 1962 1963

    it 'creates the repository if it is a fork' do
      expect(project).to receive(:forked?).and_return(true)
1964 1965
      expect(project).to receive(:repository_exists?).and_return(false)
      expect(project.repository).to receive(:create_repository) { true }
1966 1967 1968

      project.ensure_repository
    end
1969 1970
  end

1971 1972 1973 1974 1975 1976 1977 1978
  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
1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995

    it 'saves the url credentials percent decoded' do
      url = 'http://user:pass%21%3F%40@github.com/t.git'
      project = build(:project, import_url: url)

      # When the credentials are not decoded this expectation fails
      expect(project.import_url).to eq(url)
      expect(project.import_data.credentials).to eq(user: 'user', password: 'pass!?@')
    end

    it 'saves url with no credentials' do
      url = 'http://github.com/t.git'
      project = build(:project, import_url: url)

      expect(project.import_url).to eq(url)
      expect(project.import_data.credentials).to eq(user: nil, password: nil)
    end
1996 1997
  end

A
Andre Guedes 已提交
1998
  describe '#container_registry_url' do
1999
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
2000

A
Andre Guedes 已提交
2001
    subject { project.container_registry_url }
K
Kamil Trzcinski 已提交
2002

2003 2004 2005
    before do
      stub_container_registry_config(**registry_settings)
    end
K
Kamil Trzcinski 已提交
2006 2007 2008

    context 'for enabled registry' do
      let(:registry_settings) do
2009 2010
        { enabled: true,
          host_port: 'example.com' }
K
Kamil Trzcinski 已提交
2011 2012
      end

2013
      it { is_expected.not_to be_nil }
K
Kamil Trzcinski 已提交
2014 2015 2016 2017
    end

    context 'for disabled registry' do
      let(:registry_settings) do
2018
        { enabled: false }
K
Kamil Trzcinski 已提交
2019 2020 2021 2022 2023 2024
      end

      it { is_expected.to be_nil }
    end
  end

2025
  describe '#has_container_registry_tags?' do
2026
    let(:project) { create(:project) }
2027 2028

    context 'when container registry is enabled' do
2029 2030 2031
      before do
        stub_container_registry_config(enabled: true)
      end
2032 2033 2034 2035 2036 2037 2038 2039 2040

      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

2041
        it 'has image tags' do
2042 2043 2044 2045 2046 2047 2048 2049 2050 2051
          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

2052
        it 'has image tags' do
2053 2054 2055 2056 2057 2058 2059 2060 2061
          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

2062
        it 'does not have image tags' do
2063 2064 2065 2066 2067 2068
          expect(project).not_to have_container_registry_tags
        end
      end
    end

    context 'when container registry is disabled' do
2069 2070 2071
      before do
        stub_container_registry_config(enabled: false)
      end
2072

2073
      it 'does not have image tags' do
2074 2075 2076
        expect(project).not_to have_container_registry_tags
      end

2077
      it 'does not check root repository tags' do
2078 2079 2080 2081
        expect(project).not_to receive(:full_path)
        expect(project).not_to have_container_registry_tags
      end

2082
      it 'iterates through container repositories' do
2083 2084 2085 2086 2087 2088
        expect(project).to receive(:container_repositories)
        expect(project).not_to have_container_registry_tags
      end
    end
  end

2089
  describe '#ci_config_path=' do
2090
    using RSpec::Parameterized::TableSyntax
2091

2092
    let(:project) { create(:project) }
2093

2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106
    where(:default_ci_config_path, :project_ci_config_path, :expected_ci_config_path) do
      nil           | :notset            | :default
      nil           | nil                | :default
      nil           | ''                 | :default
      nil           | "cust\0om/\0/path" | 'custom//path'
      ''            | :notset            | :default
      ''            | nil                | :default
      ''            | ''                 | :default
      ''            | "cust\0om/\0/path" | 'custom//path'
      'global/path' | :notset            | 'global/path'
      'global/path' | nil                | :default
      'global/path' | ''                 | :default
      'global/path' | "cust\0om/\0/path" | 'custom//path'
2107 2108
    end

2109 2110 2111
    with_them do
      before do
        stub_application_setting(default_ci_config_path: default_ci_config_path)
2112

2113 2114 2115 2116
        if project_ci_config_path != :notset
          project.ci_config_path = project_ci_config_path
        end
      end
2117

2118 2119 2120
      it 'returns the correct path' do
        expect(project.ci_config_path.presence || :default).to eq(expected_ci_config_path)
      end
2121 2122 2123
    end
  end

2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145
  describe '#uses_default_ci_config?' do
    let(:project) { build(:project)}

    it 'has a custom ci config path' do
      project.ci_config_path = 'something_custom'

      expect(project.uses_default_ci_config?).to be_falsey
    end

    it 'has a blank ci config path' do
      project.ci_config_path = ''

      expect(project.uses_default_ci_config?).to be_truthy
    end

    it 'does not have a custom ci config path' do
      project.ci_config_path = nil

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

2146
  describe '#latest_successful_build_for_ref' do
2147
    let(:project) { create(:project, :repository) }
2148
    let(:pipeline) { create_pipeline(project) }
L
Lin Jen-Shin 已提交
2149

2150
    it_behaves_like 'latest successful build for sha or ref'
L
Lin Jen-Shin 已提交
2151

2152
    subject { project.latest_successful_build_for_ref(build_name) }
2153

2154 2155
    context 'with a specified ref' do
      let(:build) { create_build }
2156

2157
      subject { project.latest_successful_build_for_ref(build.name, project.default_branch) }
2158

2159
      it { is_expected.to eq(build) }
2160 2161
    end
  end
2162

2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199
  describe '#latest_pipeline_for_ref' do
    let(:project) { create(:project, :repository) }
    let(:second_branch) { project.repository.branches[2] }

    let!(:pipeline_for_default_branch) do
      create(:ci_empty_pipeline, project: project, sha: project.commit.id,
                                 ref: project.default_branch)
    end
    let!(:pipeline_for_second_branch) do
      create(:ci_empty_pipeline, project: project, sha: second_branch.target,
                                 ref: second_branch.name)
    end

    before do
      create(:ci_empty_pipeline, project: project, sha: project.commit.parent.id,
                                 ref: project.default_branch)
    end

    context 'default repository branch' do
      subject { project.latest_pipeline_for_ref(project.default_branch) }

      it { is_expected.to eq(pipeline_for_default_branch) }
    end

    context 'provided ref' do
      subject { project.latest_pipeline_for_ref(second_branch.name) }

      it { is_expected.to eq(pipeline_for_second_branch) }
    end

    context 'bad ref' do
      subject { project.latest_pipeline_for_ref(SecureRandom.uuid) }

      it { is_expected.to be_nil }
    end
  end

2200 2201 2202
  describe '#latest_successful_build_for_sha' do
    let(:project) { create(:project, :repository) }
    let(:pipeline) { create_pipeline(project) }
2203

2204
    it_behaves_like 'latest successful build for sha or ref'
2205

2206
    subject { project.latest_successful_build_for_sha(build_name, project.commit.sha) }
2207 2208
  end

2209
  describe '#latest_successful_build_for_ref!' do
2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221
    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')

2222
        expect(project.latest_successful_build_for_ref!(build1_p2.name))
2223 2224 2225 2226 2227 2228 2229 2230 2231
          .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
2232
          expect(project.latest_successful_build_for_ref!(build.name))
2233 2234 2235 2236
            .to eq(build)
        end

        it 'returns exception if the build cannot be found' do
2237
          expect { project.latest_successful_build_for_ref!(build.name, 'TAIL') }
2238
            .to raise_error(ActiveRecord::RecordNotFound)
2239
        end
2240 2241
      end

L
Lin Jen-Shin 已提交
2242
      context 'with some pending pipeline' do
2243
        before do
2244
          create_build(create_pipeline(project, 'pending'))
2245 2246
        end

L
Lin Jen-Shin 已提交
2247
        it 'gives the latest build from latest pipeline' do
2248
          expect(project.latest_successful_build_for_ref!(build.name))
2249
            .to eq(build)
2250
        end
2251 2252 2253 2254
      end
    end

    context 'with pending pipeline' do
2255
      it 'returns empty relation' do
2256
        pipeline.update(status: 'pending')
2257
        pending_build = create_build(pipeline)
2258

2259
        expect { project.latest_successful_build_for_ref!(pending_build.name) }
2260
          .to raise_error(ActiveRecord::RecordNotFound)
2261 2262 2263 2264
      end
    end
  end

2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279
  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
2280 2281 2282 2283
    end
  end

  describe '#jira_import_status' do
2284
    let(:project) { create(:project, import_type: 'jira') }
2285

2286
    context 'when no jira imports' do
2287
      it 'returns none' do
2288
        expect(project.jira_import_status).to eq('initial')
2289 2290 2291
      end
    end

2292 2293 2294
    context 'when there are jira imports' do
      let(:jira_import1) { build(:jira_import_state, :finished, project: project) }
      let(:jira_import2) { build(:jira_import_state, project: project) }
2295

2296 2297
      before do
        expect(project).to receive(:latest_jira_import).and_return(jira_import2)
2298 2299
      end

2300 2301 2302
      context 'when latest import status is initial or jira imports are mising' do
        it 'returns initial' do
          expect(project.jira_import_status).to eq('initial')
2303
        end
2304
      end
2305

2306
      context 'when latest import status is scheduled' do
2307
        before do
2308
          jira_import2.schedule!
2309 2310
        end

2311 2312
        it 'returns scheduled' do
          expect(project.jira_import_status).to eq('scheduled')
2313 2314
        end
      end
2315 2316 2317 2318 2319 2320 2321 2322
    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)

2323
        expect(project.human_import_status_name).to eq(_('started'))
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
      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

2336
  describe '#add_import_job' do
2337 2338
    let(:import_jid) { '123' }

2339
    context 'forked' do
2340 2341 2342 2343 2344 2345
      let(:forked_from_project) { create(:project, :repository) }
      let(:project) { create(:project) }

      before do
        fork_project(forked_from_project, nil, target_project: project)
      end
2346 2347

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

2350
        expect(project.add_import_job).to eq(import_jid)
2351
      end
2352 2353 2354 2355 2356 2357 2358 2359 2360

      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
2361 2362 2363 2364
    end

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

2367 2368
        expect(RepositoryImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
        expect(project.add_import_job).to eq(import_jid)
2369 2370
      end
    end
2371 2372 2373 2374

    context 'jira import' do
      it 'schedules a jira import job' do
        project = create(:project, import_type: 'jira')
2375
        jira_import = create(:jira_import_state, project: project)
2376 2377

        expect(Gitlab::JiraImport::Stage::StartImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid)
2378 2379 2380 2381

        jira_import.schedule!

        expect(jira_import.jid).to eq(import_jid)
2382 2383 2384 2385 2386
      end
    end
  end

  describe '#jira_import?' do
2387 2388
    let_it_be(:project) { build(:project, import_type: 'jira') }
    let_it_be(:jira_import) { build(:jira_import_state, project: project) }
2389

2390 2391
    before do
      expect(project).to receive(:jira_imports).and_return([jira_import])
2392 2393
    end

2394 2395
    it { expect(project.jira_import?).to be true }
    it { expect(project.import?).to be true }
2396 2397 2398
  end

  describe '#remove_import_data' do
2399
    let_it_be(:import_data) { ProjectImportData.new(data: { 'test' => 'some data' }) }
2400 2401

    context 'when jira import' do
2402 2403
      let_it_be(:project, reload: true) { create(:project, import_type: 'jira', import_data: import_data) }
      let_it_be(:jira_import) { create(:jira_import_state, project: project) }
2404

2405
      it 'does remove import data' do
2406 2407
        expect(project.mirror?).to be false
        expect(project.jira_import?).to be true
2408
        expect { project.remove_import_data }.to change { ProjectImportData.count }.by(-1)
2409 2410 2411
      end
    end

2412 2413 2414
    context 'when neither a mirror nor a jira import' do
      let_it_be(:user) { create(:user) }
      let_it_be(:project) { create(:project, import_type: 'github', import_data: import_data) }
2415 2416 2417 2418 2419 2420 2421

      it 'removes import data' do
        expect(project.mirror?).to be false
        expect(project.jira_import?).to be false
        expect { project.remove_import_data }.to change { ProjectImportData.count }.by(-1)
      end
    end
2422 2423
  end

R
Rémy Coutable 已提交
2424
  describe '#gitlab_project_import?' do
2425
    subject(:project) { build(:project, import_type: 'gitlab_project') }
R
Rémy Coutable 已提交
2426 2427 2428 2429 2430

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

  describe '#gitea_import?' do
2431
    subject(:project) { build(:project, import_type: 'gitea') }
R
Rémy Coutable 已提交
2432 2433 2434 2435

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

2436
  describe '#has_remote_mirror?' do
2437
    let(:project) { create(:project, :remote_mirror, :import_started) }
2438

2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449
    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
L
Lin Jen-Shin 已提交
2450
      project.remote_mirrors.first.update(enabled: false)
2451 2452 2453 2454 2455 2456 2457

      is_expected.to be_falsy
    end
  end

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

2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480
    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
L
Lin Jen-Shin 已提交
2481
      project.remote_mirrors.first.update(enabled: false)
2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514

      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

2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529
  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

2530
  describe '#ancestors_upto' do
2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542
    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
2543 2544 2545 2546 2547 2548 2549 2550 2551 2552

    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
2553 2554
  end

2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568
  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

2569
    context 'in a nested group' do
2570 2571 2572 2573 2574 2575
      let(:root) { create(:group) }
      let(:child) { create(:group, parent: root) }
      let(:project) { create(:project, group: child) }

      it { is_expected.to eq(root) }
    end
2576 2577
  end

2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605
  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
  end

2606
  describe '#lfs_enabled?' do
2607
    let(:project) { create(:project) }
2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667

    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

J
Jacopo 已提交
2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679
  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

2680
  describe '#change_head' do
2681
    let(:project) { create(:project, :repository) }
2682

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

2688
    it 'calls the before_change_head and after_change_head methods' do
2689
      expect(project.repository).to receive(:before_change_head)
2690 2691
      expect(project.repository).to receive(:after_change_head)

2692 2693 2694
      project.change_head(project.default_branch)
    end

2695 2696 2697 2698 2699 2700
    it 'updates commit count' do
      expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:commit_count])

      project.change_head(project.default_branch)
    end

2701 2702 2703 2704 2705 2706 2707 2708 2709 2710
    it 'copies the gitattributes' do
      expect(project.repository).to receive(:copy_gitattributes).with(project.default_branch)
      project.change_head(project.default_branch)
    end

    it 'reloads the default branch' do
      expect(project).to receive(:reload_default_branch)
      project.change_head(project.default_branch)
    end
  end
Y
Yorick Peterse 已提交
2711

2712
  context 'forks' do
B
Bob Van Landuyt 已提交
2713 2714
    include ProjectForksHelper

2715 2716 2717 2718 2719
    let(:project) { create(:project, :public) }
    let!(:forked_project) { fork_project(project) }

    describe '#fork_network' do
      it 'includes a fork of the project' do
B
Bob Van Landuyt 已提交
2720
        expect(project.fork_network.projects).to include(forked_project)
2721 2722 2723 2724 2725
      end

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

B
Bob Van Landuyt 已提交
2726
        expect(project.fork_network.projects).to include(other_fork)
2727 2728 2729 2730 2731
      end

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

B
Bob Van Landuyt 已提交
2732
        expect(forked_project.fork_network.projects).to include(other_fork)
2733 2734 2735
      end

      it 'includes the base project' do
B
Bob Van Landuyt 已提交
2736
        expect(forked_project.fork_network.projects).to include(project.reload)
2737 2738 2739 2740 2741 2742 2743 2744
      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

2745
      it 'is true for a fork of a fork' do
2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762
        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
2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775

    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
2776 2777 2778 2779

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

2782 2783 2784 2785 2786 2787
    describe '#forks' do
      it 'includes direct forks of the project' do
        expect(project.forks).to contain_exactly(forked_project)
      end
    end

2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804
    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
2805 2806 2807 2808

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

2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820
      context 'when LFS object is only associated to the source' do
        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
2821 2822
      end

2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834
      context 'when LFS object is only associated to the fork' do
        before do
          forked_project.lfs_objects << lfs_object
        end

        it 'returns nothing' do
          expect(project.all_lfs_objects).to be_empty
        end

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

2837 2838 2839 2840 2841 2842 2843 2844 2845 2846
      context 'when LFS object is associated to both source and fork' do
        before do
          project.lfs_objects << lfs_object
          forked_project.lfs_objects << lfs_object
        end

        it 'returns the lfs object for the source and fork' do
          expect(project.all_lfs_objects).to contain_exactly(lfs_object)
          expect(forked_project.all_lfs_objects).to contain_exactly(lfs_object)
        end
2847 2848
      end
    end
2849 2850
  end

2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864
  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

J
John Cai 已提交
2865 2866 2867 2868 2869 2870 2871 2872 2873 2874
  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

2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912
  describe '#change_repository_storage' do
    let(:project) { create(:project, :repository) }
    let(:read_only_project) { create(:project, :repository, repository_read_only: true) }

    before do
      stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
    end

    it 'schedules the transfer of the repository to the new storage and locks the project' do
      expect(ProjectUpdateRepositoryStorageWorker).to receive(:perform_async).with(project.id, 'test_second_storage')

      project.change_repository_storage('test_second_storage')
      project.save!

      expect(project).to be_repository_read_only
    end

    it "doesn't schedule the transfer if the repository is already read-only" do
      expect(ProjectUpdateRepositoryStorageWorker).not_to receive(:perform_async)

      read_only_project.change_repository_storage('test_second_storage')
      read_only_project.save!
    end

    it "doesn't lock or schedule the transfer if the storage hasn't changed" do
      expect(ProjectUpdateRepositoryStorageWorker).not_to receive(:perform_async)

      project.change_repository_storage(project.repository_storage)
      project.save!

      expect(project).not_to be_repository_read_only
    end

    it 'throws an error if an invalid repository storage is provided' do
      expect { project.change_repository_storage('unknown') }.to raise_error(ArgumentError)
    end
  end

Y
Yorick Peterse 已提交
2913
  describe '#pushes_since_gc' do
2914
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935

    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
2936
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949

    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
2950
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963

    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
2964

2965
  describe '#deployment_variables' do
2966 2967
    let(:project) { create(:project) }
    let(:environment) { 'production' }
2968
    let(:namespace) { 'namespace' }
2969

2970
    subject { project.deployment_variables(environment: environment, kubernetes_namespace: namespace) }
2971 2972 2973 2974

    before do
      expect(project).to receive(:deployment_platform).with(environment: environment)
        .and_return(deployment_platform)
2975 2976
    end

2977 2978
    context 'when project has no deployment platform' do
      let(:deployment_platform) { nil }
2979

2980
      it { is_expected.to eq [] }
2981 2982
    end

2983 2984 2985
    context 'when project has a deployment platform' do
      let(:platform_variables) { %w(platform variables) }
      let(:deployment_platform) { double }
J
James Fargher 已提交
2986

2987 2988
      before do
        expect(deployment_platform).to receive(:predefined_variables)
2989
          .with(project: project, environment_name: environment, kubernetes_namespace: namespace)
2990
          .and_return(platform_variables)
2991 2992
      end

2993
      it { is_expected.to eq platform_variables }
2994 2995 2996
    end
  end

2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018
  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

3019
  describe '#ci_variables_for' do
3020
    let(:project) { create(:project) }
3021
    let(:environment_scope) { '*' }
3022

3023
    let!(:ci_variable) do
3024
      create(:ci_variable, value: 'secret', project: project, environment_scope: environment_scope)
3025 3026 3027 3028 3029 3030
    end

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

3031
    subject { project.reload.ci_variables_for(ref: 'ref') }
L
Lin Jen-Shin 已提交
3032 3033 3034 3035 3036

    before do
      stub_application_setting(
        default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end
3037 3038 3039

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
3040
        is_expected.to contain_exactly(ci_variable, protected_variable)
3041 3042 3043
      end
    end

3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056
    it 'memoizes the result by ref and environment', :request_store do
      scoped_variable = create(:ci_variable, value: 'secret', project: project, environment_scope: 'scoped')

      expect(project).to receive(:protected_for?).with('ref').once.and_return(true)
      expect(project).to receive(:protected_for?).with('other').twice.and_return(false)

      2.times do
        expect(project.reload.ci_variables_for(ref: 'ref', environment: 'production')).to contain_exactly(ci_variable, protected_variable)
        expect(project.reload.ci_variables_for(ref: 'other')).to contain_exactly(ci_variable)
        expect(project.reload.ci_variables_for(ref: 'other', environment: 'scoped')).to contain_exactly(ci_variable, scoped_variable)
      end
    end

3057
    context 'when the ref is not protected' do
3058 3059 3060 3061
      before do
        allow(project).to receive(:protected_for?).with('ref').and_return(false)
      end

3062 3063
      it 'contains only the CI variables' do
        is_expected.to contain_exactly(ci_variable)
3064 3065 3066
      end
    end

3067 3068
    context 'when the ref is a protected branch' do
      before do
3069
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
3070
      end
3071 3072 3073 3074 3075 3076

      it_behaves_like 'ref is protected'
    end

    context 'when the ref is a protected tag' do
      before do
3077
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
3078 3079 3080
      end

      it_behaves_like 'ref is protected'
3081
    end
3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171

    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
3172 3173
  end

3174
  describe '#any_lfs_file_locks?', :request_store do
3175
    let_it_be(:project) { create(:project) }
3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189

    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

3190
  describe '#protected_for?' do
3191
    let(:project) { create(:project, :repository) }
3192

3193
    subject { project.protected_for?(ref) }
3194

3195
    shared_examples 'ref is not protected' do
3196 3197 3198 3199 3200 3201
      before do
        stub_application_setting(
          default_branch_protection: Gitlab::Access::PROTECTION_NONE)
      end

      it 'returns false' do
3202
        is_expected.to be false
3203 3204 3205
      end
    end

3206
    shared_examples 'ref is protected branch' do
3207
      before do
3208
        create(:protected_branch, name: 'master', project: project)
3209 3210 3211
      end

      it 'returns true' do
3212
        is_expected.to be true
3213 3214 3215
      end
    end

3216
    shared_examples 'ref is protected tag' do
3217
      before do
3218
        create(:protected_tag, name: 'v1.0.0', project: project)
3219 3220 3221
      end

      it 'returns true' do
3222
        is_expected.to be true
3223 3224
      end
    end
3225

3226 3227
    context 'when ref is nil' do
      let(:ref) { nil }
3228

3229
      it 'returns false' do
3230
        is_expected.to be false
3231
      end
3232 3233 3234 3235 3236
    end

    context 'when ref is ref name' do
      context 'when ref is ambiguous' do
        let(:ref) { 'ref' }
3237 3238

        before do
3239 3240
          project.repository.add_branch(project.creator, 'ref', 'master')
          project.repository.add_tag(project.creator, 'ref', 'master')
3241 3242
        end

3243 3244
        it 'raises an error' do
          expect { subject }.to raise_error(Repository::AmbiguousRefError)
3245 3246
        end
      end
3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264

      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
3265 3266 3267 3268 3269 3270 3271 3272

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

        it 'returns false' do
          is_expected.to be false
        end
      end
3273 3274
    end

3275 3276 3277
    context 'when ref is full ref' do
      context 'when the ref is not protected' do
        let(:ref) { 'refs/heads/master' }
3278

3279
        it_behaves_like 'ref is not protected'
3280 3281
      end

3282 3283 3284 3285 3286 3287 3288 3289 3290 3291
      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'
3292 3293
      end

3294 3295 3296
      context 'when branch ref name is a full tag ref' do
        let(:ref) { 'refs/tags/something' }

3297
        before do
3298
          project.repository.add_branch(project.creator, ref, 'master')
3299 3300
        end

3301 3302
        context 'when ref is not protected' do
          it 'returns false' do
3303
            is_expected.to be false
3304 3305 3306 3307 3308 3309 3310 3311 3312
          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
3313
            is_expected.to be true
3314
          end
3315 3316
        end
      end
3317 3318 3319 3320 3321 3322 3323

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

        it 'returns false' do
          is_expected.to be false
        end
3324 3325 3326 3327
      end
    end
  end

M
Markus Koller 已提交
3328
  describe '#update_project_statistics' do
3329
    let(:project) { create(:project) }
M
Markus Koller 已提交
3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347

    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

3348
  describe 'inside_path' do
3349 3350 3351
    let!(:project1) { create(:project, namespace: create(:namespace, path: 'name_pace')) }
    let!(:project2) { create(:project) }
    let!(:project3) { create(:project, namespace: create(:namespace, path: 'namespace')) }
3352
    let!(:path) { project1.namespace.full_path }
3353

3354
    it 'returns correct project' do
3355
      expect(described_class.inside_path(path)).to eq([project1])
3356
    end
3357 3358
  end

D
Douwe Maan 已提交
3359
  describe '#route_map_for' do
3360
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
3361 3362 3363 3364 3365 3366 3367 3368
    let(:route_map) do
      <<-MAP.strip_heredoc
      - source: /source/(.*)/
        public: '\\1'
      MAP
    end

    before do
3369
      project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
D
Douwe Maan 已提交
3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396
    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
3397
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421
    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
3422 3423 3424 3425 3426 3427 3428 3429 3430 3431

      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
D
Douwe Maan 已提交
3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444
    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

3445
  describe '#parent' do
3446
    let(:project) { create(:project) }
3447 3448 3449 3450

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

3451 3452 3453 3454 3455 3456
  describe '#parent_id' do
    let(:project) { create(:project) }

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

3457
  describe '#parent_changed?' do
3458
    let(:project) { create(:project) }
3459

3460 3461 3462
    before do
      project.namespace_id = 7
    end
3463 3464 3465 3466

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

3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514
  describe '#default_merge_request_target' do
    context 'when forked from a more visible project' do
      it 'returns the more restrictive project' do
        project = create(:project, :public)
        forked = fork_project(project)
        forked.visibility = Gitlab::VisibilityLevel::PRIVATE
        forked.save!

        expect(project.visibility).to eq 'public'
        expect(forked.visibility).to eq 'private'

        expect(forked.default_merge_request_target).to eq(forked)
      end
    end

    context 'when forked from a project with disabled merge requests' do
      it 'returns the current project' do
        project = create(:project, :merge_requests_disabled)
        forked = fork_project(project)

        expect(forked.forked_from_project).to receive(:merge_requests_enabled?)
          .and_call_original

        expect(forked.default_merge_request_target).to eq(forked)
      end
    end

    context 'when forked from a project with enabled merge requests' do
      it 'returns the source project' do
        project = create(:project, :public)
        forked = fork_project(project)

        expect(project.visibility).to eq 'public'
        expect(forked.visibility).to eq 'public'

        expect(forked.default_merge_request_target).to eq(project)
      end
    end

    context 'when not forked' do
      it 'returns the current project' do
        project = build_stubbed(:project)

        expect(project.default_merge_request_target).to eq(project)
      end
    end
  end

3515 3516 3517
  def enable_lfs
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
  end
K
Kamil Trzcinski 已提交
3518

3519
  describe '#pages_url' do
3520 3521
    let(:group) { create(:group, name: 'Group') }
    let(:nested_group) { create(:group, parent: group) }
K
Kamil Trzcinski 已提交
3522 3523 3524 3525 3526 3527 3528 3529 3530
    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

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

3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544
      context 'group page' do
        let(:project_name) { 'group.example.com' }

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

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

        it { is_expected.to eq("http://group.example.com/project") }
      end
K
Kamil Trzcinski 已提交
3545 3546
    end

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

3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561
      context 'group page' do
        let(:project_name) { 'group.example.com' }

        it { is_expected.to eq(expected_url) }
      end

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

        it { is_expected.to eq(expected_url) }
      end
K
Kamil Trzcinski 已提交
3562 3563
    end
  end
3564

3565 3566 3567
  describe '#lfs_http_url_to_repo' do
    let(:project) { create(:project) }

3568 3569 3570 3571 3572 3573 3574 3575
    context 'when a custom HTTP clone URL root is not set' do
      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
3576

3577 3578 3579 3580 3581 3582 3583 3584 3585 3586
    context 'when a custom HTTP clone URL root is set' do
      before do
        stub_application_setting(custom_http_clone_url_root: 'https://git.example.com:51234')
      end

      it 'returns the url to the repo, with the root replaced with the custom one' do
        lfs_http_url_to_repo = project.lfs_http_url_to_repo('operation_that_doesnt_matter')

        expect(lfs_http_url_to_repo).to eq("https://git.example.com:51234/#{project.full_path}.git")
      end
3587 3588 3589
    end
  end

3590
  describe '#pipeline_status' do
3591
    let(:project) { create(:project, :repository) }
3592

3593
    it 'builds a pipeline status' do
3594
      expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
3595 3596 3597 3598 3599 3600
    end

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

3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619
  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

3620 3621
  describe '#last_repository_updated_at' do
    it 'sets to created_at upon creation' do
3622
      project = create(:project, created_at: 2.hours.ago)
3623 3624 3625 3626

      expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
    end
  end
3627 3628 3629 3630 3631

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

    let!(:private_project) do
3632
      create(:project, :private, creator: user, namespace: user.namespace)
3633 3634
    end

3635
    let!(:public_project) { create(:project, :public) }
3636 3637 3638

    context 'with a user' do
      let(:projects) do
3639
        described_class.all.public_or_visible_to_user(user)
3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652
      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
3653
        projects = described_class.all.public_or_visible_to_user
3654 3655 3656 3657

        expect(projects).to eq([public_project])
      end
    end
3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671

    context 'min_access_level' do
      let!(:private_project) { create(:project, :private) }

      before do
        private_project.add_guest(user)
      end

      it 'excludes projects when user does not have required minimum access level' do
        projects = described_class.all.public_or_visible_to_user(user, Gitlab::Access::REPORTER)

        expect(projects).to contain_exactly(public_project)
      end
    end
3672
  end
3673

3674
  describe '.ids_with_issuables_available_for' do
3675 3676 3677 3678 3679 3680 3681 3682 3683
    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 )

3684
      project_ids = described_class.ids_with_issuables_available_for(user).pluck(:id)
3685 3686 3687 3688 3689 3690

      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

3691
  describe '.with_feature_available_for_user' do
3692 3693
    let(:user) { create(:user) }
    let(:feature) { MergeRequest }
3694 3695 3696

    subject { described_class.with_feature_available_for_user(feature, user) }

3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729
    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
3730

3731
    context 'with user' do
3732 3733 3734 3735
      before do
        project.add_guest(user)
      end

3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746
      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)
3747 3748 3749
          end
        end

3750 3751 3752
        context 'when user has access to the feature' do
          it 'returns projects with the project feature private' do
            project.add_reporter(user)
3753 3754 3755 3756 3757

            is_expected.to include(project)
          end
        end
      end
3758
    end
3759

3760 3761
    context 'user is an admin' do
      let(:user) { create(:user, :admin) }
3762

3763 3764 3765 3766
      it_behaves_like 'feature disabled'
      it_behaves_like 'feature public'
      it_behaves_like 'feature enabled'
      it_behaves_like 'feature access level is nil'
3767

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

3771 3772
        it 'returns projects with the project feature private' do
          is_expected.to include(project)
3773 3774 3775 3776
        end
      end
    end

3777 3778
    context 'without user' do
      let(:user) { nil }
3779

3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790
      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
3791 3792 3793 3794
      end
    end
  end

3795 3796 3797 3798 3799
  describe '.filter_by_feature_visibility' do
    include_context 'ProjectPolicyTable context'
    include ProjectHelpers
    using RSpec::Parameterized::TableSyntax

3800
    let_it_be(:group) { create(:group) }
3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822
    let!(:project) { create(:project, project_level, namespace: group ) }
    let(:user) { create_user_from_membership(project, membership) }

    context 'reporter level access' do
      let(:feature) { MergeRequest }

      where(:project_level, :feature_access_level, :membership, :expected_count) do
        permission_table_for_reporter_feature_access
      end

      with_them do
        it "respects visibility" do
          update_feature_access_level(project, feature_access_level)

          expected_objects = expected_count == 1 ? [project] : []

          expect(
            described_class.filter_by_feature_visibility(feature, user)
          ).to eq(expected_objects)
        end
      end
    end
3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882

    context 'issues' do
      let(:feature) { Issue }

      where(:project_level, :feature_access_level, :membership, :expected_count) do
        permission_table_for_guest_feature_access
      end

      with_them do
        it "respects visibility" do
          update_feature_access_level(project, feature_access_level)

          expected_objects = expected_count == 1 ? [project] : []

          expect(
            described_class.filter_by_feature_visibility(feature, user)
          ).to eq(expected_objects)
        end
      end
    end

    context 'wiki' do
      let(:feature) { :wiki }

      where(:project_level, :feature_access_level, :membership, :expected_count) do
        permission_table_for_guest_feature_access
      end

      with_them do
        it "respects visibility" do
          update_feature_access_level(project, feature_access_level)

          expected_objects = expected_count == 1 ? [project] : []

          expect(
            described_class.filter_by_feature_visibility(feature, user)
          ).to eq(expected_objects)
        end
      end
    end

    context 'code' do
      let(:feature) { :repository }

      where(:project_level, :feature_access_level, :membership, :expected_count) do
        permission_table_for_guest_feature_access_and_non_private_project_only
      end

      with_them do
        it "respects visibility" do
          update_feature_access_level(project, feature_access_level)

          expected_objects = expected_count == 1 ? [project] : []

          expect(
            described_class.filter_by_feature_visibility(feature, user)
          ).to eq(expected_objects)
        end
      end
    end
3883 3884
  end

3885
  describe '.wrap_with_cte' do
3886 3887 3888 3889 3890 3891 3892 3893 3894 3895
    let!(:user) { create(:user) }

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

    let!(:public_project) { create(:project, :public) }

    let(:projects) { described_class.all.public_or_visible_to_user(user) }

3896
    subject { described_class.wrap_with_cte(projects) }
3897 3898

    it 'wrapped query matches original' do
3899
      expect(subject.to_sql).to match(/^WITH "projects_cte" AS/)
3900 3901 3902 3903
      expect(subject).to match_array(projects)
    end
  end

3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921
  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) }

3922
      it { is_expected.to be(true) }
3923 3924 3925
    end
  end

3926
  describe '#remove_private_deploy_keys' do
3927
    let!(:project) { create(:project) }
3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942

    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
3943
          another_project = create(:project)
3944 3945
          create(:deploy_keys_project, deploy_key: key, project: another_project)
        end
3946

3947 3948
        it 'does not remove the key' do
          project.remove_private_deploy_keys
3949

3950 3951 3952 3953 3954 3955 3956 3957
          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) }
3958

3959 3960
      it 'does not remove the key' do
        project.remove_private_deploy_keys
3961

3962 3963
        expect(project.deploy_keys).to include(key)
      end
3964 3965
    end
  end
3966

3967
  describe '#remove_pages' do
3968 3969
    let(:project) { create(:project).tap { |project| project.mark_pages_as_deployed } }
    let(:pages_metadatum) { project.pages_metadatum }
3970
    let(:namespace) { project.namespace }
3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981
    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

3982
    it 'removes the pages directory and marks the project as not having pages deployed' do
3983 3984 3985 3986
      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)

3987
      expect { project.remove_pages }.to change { pages_metadatum.reload.deployed }.from(true).to(false)
3988 3989
    end

3990
    it 'is a no-op when there is no namespace' do
3991 3992
      project.namespace.delete
      project.reload
3993 3994 3995 3996

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

3997
      expect { project.remove_pages }.not_to change { pages_metadatum.reload.deployed }
3998
    end
3999 4000 4001 4002

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

4003
      expect { project.destroy }.not_to raise_error
4004
    end
4005 4006
  end

4007
  describe '#remove_export' do
4008 4009
    let(:project) { create(:project, :with_export) }

4010 4011 4012 4013 4014 4015
    before do
      allow_next_instance_of(ProjectExportWorker) do |job|
        allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
      end
    end

4016
    it 'removes the export' do
4017 4018
      project.remove_exports

J
James Lopez 已提交
4019
      expect(project.export_file_exists?).to be_falsey
4020 4021 4022
    end
  end

4023 4024 4025 4026
  describe '#forks_count' do
    it 'returns the number of forks' do
      project = build(:project)

F
Francisco Lopez 已提交
4027
      expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1)
4028 4029 4030 4031

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

4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059
  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

4060
  context 'legacy storage' do
4061
    let_it_be(:project) { create(:project, :repository, :legacy_storage) }
4062
    let(:gitlab_shell) { Gitlab::Shell.new }
4063
    let(:project_storage) { project.send(:storage) }
4064

4065 4066
    before do
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
4067
      stub_application_setting(hashed_storage_enabled: false)
4068 4069
    end

4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081
    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

4082 4083
    describe '#legacy_storage?' do
      it 'returns true when storage_version is nil' do
4084
        project = build(:project, storage_version: nil)
4085 4086 4087

        expect(project.legacy_storage?).to be_truthy
      end
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097

      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
4098
        expect(project.hashed_storage?(:repository)).to be_falsey
4099
      end
4100 4101
    end

4102 4103 4104 4105 4106
    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
4107 4108

    describe '#migrate_to_hashed_storage!' do
G
Gabriel Mazetto 已提交
4109 4110
      let(:project) { create(:project, :empty_repo, :legacy_storage) }

4111 4112 4113 4114
      it 'returns true' do
        expect(project.migrate_to_hashed_storage!).to be_truthy
      end

G
Gabriel Mazetto 已提交
4115 4116
      it 'does not run validation' do
        expect(project).not_to receive(:valid?)
4117 4118 4119 4120

        project.migrate_to_hashed_storage!
      end

4121
      it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do
4122
        Gitlab::ReferenceCounter.new(Gitlab::GlRepository::PROJECT.identifier_for_container(project)).increase
4123

4124
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
4125 4126 4127 4128

        project.migrate_to_hashed_storage!
      end

4129
      it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do
4130
        Gitlab::ReferenceCounter.new(Gitlab::GlRepository::WIKI.identifier_for_container(project)).increase
4131

4132
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
4133 4134 4135 4136

        project.migrate_to_hashed_storage!
      end

4137 4138
      it 'schedules HashedStorage::ProjectMigrateWorker' do
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_async).with(project.id)
4139 4140 4141 4142

        project.migrate_to_hashed_storage!
      end
    end
4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156

    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
4157 4158 4159
  end

  context 'hashed storage' do
4160
    let_it_be(:project) { create(:project, :repository, skip_disk_validation: true) }
4161
    let(:gitlab_shell) { Gitlab::Shell.new }
4162
    let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
N
Nick Thomas 已提交
4163 4164
    let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
    let(:hashed_path) { File.join(hashed_prefix, hash) }
4165

4166 4167 4168 4169 4170 4171 4172
    describe '#legacy_storage?' do
      it 'returns false' do
        expect(project.legacy_storage?).to be_falsey
      end
    end

    describe '#hashed_storage?' do
4173 4174
      it 'returns true if rolled out' do
        expect(project.hashed_storage?(:attachments)).to be_truthy
4175 4176
      end

4177 4178
      it 'returns false when not rolled out yet' do
        project.storage_version = 1
4179

4180
        expect(project.hashed_storage?(:attachments)).to be_falsey
4181 4182 4183
      end
    end

4184 4185
    describe '#base_dir' do
      it 'returns base_dir based on hash of project id' do
N
Nick Thomas 已提交
4186
        expect(project.base_dir).to eq(hashed_prefix)
4187 4188 4189 4190
      end
    end

    describe '#disk_path' do
4191
      it 'returns disk_path based on hash of project id' do
4192 4193 4194 4195
        expect(project.disk_path).to eq(hashed_path)
      end
    end

4196 4197 4198 4199 4200
    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
4201 4202

    describe '#migrate_to_hashed_storage!' do
G
Gabriel Mazetto 已提交
4203 4204
      let(:project) { create(:project, :repository, skip_disk_validation: true) }

4205 4206 4207 4208
      it 'returns nil' do
        expect(project.migrate_to_hashed_storage!).to be_nil
      end

T
Toon Claes 已提交
4209
      it 'does not flag as read-only' do
4210 4211
        expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only }
      end
4212 4213

      context 'when partially migrated' do
G
Gabriel Mazetto 已提交
4214
        it 'enqueues a job' do
4215 4216
          project = create(:project, storage_version: 1, skip_disk_validation: true)

G
Gabriel Mazetto 已提交
4217
          Sidekiq::Testing.fake! do
4218
            expect { project.migrate_to_hashed_storage! }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1)
G
Gabriel Mazetto 已提交
4219
          end
4220 4221
        end
      end
4222
    end
4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242

    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
4243
          expect { project.rollback_to_legacy_storage! }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1)
4244 4245 4246
        end
      end
    end
4247 4248
  end

4249
  describe '#has_ci?' do
4250
    let_it_be(:project, reload: true) { create(:project) }
4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271
    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

4272 4273
      it "CI is available" do
        expect(project).to have_ci
4274 4275
      end

4276
      context 'when auto devops is disabled' do
4277
        before do
4278
          stub_application_setting(auto_devops_enabled: false)
4279 4280
        end

4281 4282
        it "CI is not available" do
          expect(project).not_to have_ci
4283 4284 4285 4286 4287 4288
        end
      end
    end
  end

  describe '#auto_devops_enabled?' do
4289 4290 4291 4292 4293
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

4294
    let_it_be(:project, reload: true) { create(:project) }
4295 4296 4297

    subject { project.auto_devops_enabled? }

4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313
    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

4314 4315 4316 4317 4318
    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

4319
      it { is_expected.to be_truthy }
4320 4321 4322 4323 4324 4325 4326 4327
    end

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

      it { is_expected.to be_falsey }
4328 4329 4330 4331 4332 4333

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

4334
        it { is_expected.to be_truthy }
4335 4336 4337 4338
      end

      context 'when explicitly disabled' do
        before do
4339
          create(:project_auto_devops, :disabled, project: project)
4340 4341
        end

4342
        it { is_expected.to be_falsey }
4343 4344 4345
      end
    end

4346 4347 4348 4349 4350 4351 4352
    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 }

4353
      before do
4354 4355
        stub_application_setting(auto_devops_enabled: instance_enabled)
        project.update!(namespace: parent_group)
4356 4357
      end

4358 4359
      context 'when enabled on parent' do
        let(:parent_group) { create(:group, :auto_devops_enabled) }
4360

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

4365 4366 4367 4368 4369
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_truthy }
        end
4370 4371
      end

4372 4373 4374 4375 4376
      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 }
4377
        end
4378

4379 4380 4381 4382 4383 4384 4385
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_falsy }
        end
      end

4386
      context 'when enabled on root parent' do
4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405
        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

4406
      context 'when disabled on root parent' do
4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423
        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
4424 4425 4426 4427
      end
    end
  end

4428
  describe '#has_auto_devops_implicitly_enabled?' do
4429
    let_it_be(:project, reload: true) { create(:project) }
4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469

    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
4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486

    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
4487 4488
  end

4489
  describe '#has_auto_devops_implicitly_disabled?' do
4490
    let_it_be(:project, reload: true) { create(:project) }
4491

4492 4493 4494 4495 4496
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515
    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
4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535

    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

4536 4537
      context 'when force_autodevops_on_by_default is enabled for the project' do
        before do
4538 4539
          create(:project_auto_devops, project: project, enabled: false)

4540 4541 4542 4543 4544 4545 4546 4547
          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

4548 4549 4550
      context 'when disabled on group' do
        it 'has auto devops implicitly disabled' do
          project.update!(namespace: create(:group, :auto_devops_disabled))
4551

4552
          expect(project).to have_auto_devops_implicitly_disabled
4553 4554 4555
        end
      end

4556 4557 4558 4559
      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)
4560

4561
          expect(project).to have_auto_devops_implicitly_disabled
4562 4563 4564 4565 4566
        end
      end
    end
  end

4567
  describe '#api_variables' do
4568
    let_it_be(:project) { create(:project) }
4569 4570 4571 4572 4573 4574

    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

4575
    it 'contains a URL variable for every supported API version' do
4576 4577 4578
      # 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|
4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590
        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

4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617
  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
4618
        expect(project.ci_pipelines).to receive(:latest_successful_for_ref).with('foo')
4619 4620 4621 4622 4623 4624

        project.latest_successful_pipeline_for('foo')
      end
    end
  end

S
Stan Hu 已提交
4625
  describe '#check_repository_path_availability' do
4626 4627 4628 4629
    let(:project) { build(:project, :repository, :legacy_storage) }

    context 'when the repository already exists' do
      let(:project) { create(:project, :repository, :legacy_storage) }
S
Stan Hu 已提交
4630

4631 4632 4633
      it 'returns false when repository already exists' do
        expect(project.check_repository_path_availability).to be_falsey
      end
4634
    end
S
Stan Hu 已提交
4635

4636
    context 'when the repository does not exist' do
4637 4638 4639
      it 'returns false when repository already exists' do
        expect(project.check_repository_path_availability).to be_truthy
      end
4640 4641 4642 4643 4644

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

        expect(project.gitlab_shell).not_to receive(:repository_exists?)
4645
        expect(project.check_repository_path_availability).to be_truthy
4646
      end
S
Stan Hu 已提交
4647 4648 4649
    end
  end

4650 4651 4652 4653 4654 4655 4656 4657 4658 4659
  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)

4660
      expect(project.ci_pipelines).to receive(:latest_successful_for_ref)
4661 4662 4663 4664 4665 4666 4667 4668 4669 4670
        .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
4671 4672

  describe '#after_import' do
4673
    let(:project) { create(:project) }
4674
    let(:import_state) { create(:import_state, project: project) }
4675 4676

    it 'runs the correct hooks' do
4677 4678
      expect(project.repository).to receive(:expire_content_cache)
      expect(project.wiki.repository).to receive(:expire_content_cache)
4679
      expect(import_state).to receive(:finish)
4680
      expect(project).to receive(:update_project_counter_caches)
4681
      expect(project).to receive(:after_create_default_branch)
4682
      expect(project).to receive(:refresh_markdown_cache!)
4683
      expect(InternalId).to receive(:flush_records!).with(project: project)
4684
      expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
4685 4686 4687

      project.after_import
    end
4688 4689 4690 4691

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

4692 4693 4694 4695
      before do
        create(:import_state, :started, project: project)
      end

4696
      it 'does not protect when branch protection is disabled' do
4697
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_NONE)
4698 4699 4700 4701 4702 4703 4704

        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
4705
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
4706 4707 4708 4709 4710 4711 4712 4713 4714

        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
4715
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728

        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)
4729 4730
        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])
4731 4732
      end
    end
4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763
  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
4764

4765
  describe '#write_repository_config' do
4766
    let_it_be(:project) { create(:project, :repository) }
4767 4768 4769 4770

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

4771
      expect(rugged_config['gitlab.fullpath']).to eq project.full_path
4772 4773 4774 4775 4776
    end

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

4777
      expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
4778 4779 4780 4781 4782 4783 4784 4785
    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
4786

4787 4788 4789 4790 4791 4792 4793 4794
  describe '#to_ability_name' do
    it 'returns project' do
      project = build(:project_empty_repo)

      expect(project.to_ability_name).to eq('project')
    end
  end

4795
  describe '#execute_hooks' do
4796
    let(:data) { { ref: 'refs/heads/master', data: 'data' } }
4797

D
Duana Saskia 已提交
4798
    it 'executes active projects hooks with the specified scope' do
4799 4800 4801 4802
      hook = create(:project_hook, merge_requests_events: false, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([hook])
4803
      project = create(:project, hooks: [hook])
4804 4805 4806

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

4807
      project.execute_hooks(data, :push_hooks)
4808 4809 4810
    end

    it 'does not execute project hooks that dont match the specified scope' do
4811
      hook = create(:project_hook, merge_requests_events: true, push_events: false)
4812 4813 4814 4815
      project = create(:project, hooks: [hook])

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

4816
      project.execute_hooks(data, :push_hooks)
4817 4818
    end

D
Duana Saskia 已提交
4819
    it 'does not execute project hooks which are not active' do
4820 4821 4822 4823
      hook = create(:project_hook, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([])
D
Duana Saskia 已提交
4824 4825 4826 4827
      project = create(:project, hooks: [hook])

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

4828
      project.execute_hooks(data, :push_hooks)
4829 4830 4831
    end

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

4834
      project = build(:project)
4835
      project.execute_hooks(data, :merge_request_hooks)
4836
    end
4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849

    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
4850
          project.execute_hooks(data, :merge_request_hooks)
4851 4852 4853
        end
      end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
    end
4854
  end
4855

S
Stan Hu 已提交
4856
  describe '#has_active_hooks?' do
4857
    let_it_be(:project) { create(:project) }
S
Stan Hu 已提交
4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872

    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
4873 4874 4875
    end

    it 'returns true when a plugin exists' do
4876
      expect(Gitlab::FileHook).to receive(:any?).twice.and_return(true)
4877 4878 4879

      expect(project.has_active_hooks?(:merge_request_events)).to be_truthy
      expect(project.has_active_hooks?).to be_truthy
S
Stan Hu 已提交
4880 4881 4882 4883
    end
  end

  describe '#has_active_services?' do
4884
    let_it_be(:project) { create(:project) }
S
Stan Hu 已提交
4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895

    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

4896 4897
  describe '#badges' do
    let(:project_group) { create(:group) }
4898
    let(:project) { create(:project, path: 'avatar', namespace: project_group) }
4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911

    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

4912 4913
    context 'with nested_groups' do
      let(:parent_group) { create(:group) }
4914

4915 4916 4917 4918
      before do
        create_list(:group_badge, 2, group: project_group)
        project_group.update(parent: parent_group)
      end
4919

4920 4921
      it 'returns the project and the project nested groups badges' do
        expect(project.badges.count).to eq 5
4922 4923 4924
      end
    end
  end
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
  context 'with cross internal project merge requests' do
    let(:project) { create(:project, :repository, :internal) }
    let(:forked_project) { fork_project(project, nil, repository: true) }
    let(:user) { double(:user) }

    it "does not endlessly loop for internal projects with MRs to each other", :sidekiq_inline do
      allow(user).to receive(:can?).and_return(true, false, true)
      allow(user).to receive(:id).and_return(1)

      create(
        :merge_request,
        target_project: project,
        target_branch: 'merge-test',
        source_project: forked_project,
        source_branch: 'merge-test',
        allow_collaboration: true
      )

      create(
        :merge_request,
        target_project: forked_project,
        target_branch: 'merge-test',
        source_project: project,
        source_branch: 'merge-test',
        allow_collaboration: true
      )

      expect(user).to receive(:can?).at_most(5).times
      project.branch_allows_collaboration?(user, "merge-test")
    end
  end

4958 4959
  context 'with cross project merge requests' do
    let(:user) { create(:user) }
4960 4961
    let(:target_project) { create(:project, :repository) }
    let(:project) { fork_project(target_project, nil, repository: true) }
4962 4963 4964 4965 4966 4967 4968 4969 4970 4971
    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
4972 4973 4974 4975
    let!(:merge_request) do
      create(
        :merge_request,
        target_project: target_project,
4976
        target_branch: 'target-branch',
4977 4978
        source_project: project,
        source_branch: 'awesome-feature-1',
4979
        allow_collaboration: true
4980 4981 4982 4983
      )
    end

    before do
4984
      target_project.add_developer(user)
4985 4986
    end

4987 4988 4989 4990
    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
4991

4992 4993
      it 'does not include closed merge requests' do
        merge_request.close
4994

4995 4996 4997 4998 4999 5000
        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)
5001

5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013
        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
5014 5015
    end

5016
    describe '#any_branch_allows_collaboration?' do
5017
      it 'allows access when there are merge requests open allowing collaboration', :sidekiq_might_not_need_inline do
5018
        expect(project.any_branch_allows_collaboration?(user))
5019 5020 5021
          .to be_truthy
      end

5022 5023 5024 5025 5026 5027 5028 5029 5030
      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
5031
      it 'allows access if the user can merge the merge request', :sidekiq_might_not_need_inline do
5032
        expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
5033 5034 5035
          .to be_truthy
      end

5036 5037 5038 5039
      it 'does not allow guest users access' do
        guest = create(:user)
        target_project.add_guest(guest)

5040
        expect(project.branch_allows_collaboration?(guest, 'awesome-feature-1'))
5041 5042 5043
          .to be_falsy
      end

5044
      it 'does not allow access to branches for which the merge request was closed' do
5045 5046
        create(:merge_request, :closed,
               target_project: target_project,
5047
               target_branch: 'target-branch',
5048 5049
               source_project: project,
               source_branch: 'rejected-feature-1',
5050
               allow_collaboration: true)
5051

5052
        expect(project.branch_allows_collaboration?(user, 'rejected-feature-1'))
5053 5054 5055
          .to be_falsy
      end

5056
      it 'does not allow access if the user cannot merge the merge request' do
5057
        create(:protected_branch, :maintainers_can_push, project: target_project, name: 'target-branch')
5058

5059
        expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
5060 5061 5062
          .to be_falsy
      end

5063
      context 'when the requeststore is active', :request_store do
5064
        it 'only queries per project across instances' do
5065
          control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') }
5066

5067
          expect { 2.times { described_class.find(project.id).branch_allows_collaboration?(user, 'awesome-feature-1') } }
5068
            .not_to exceed_query_limit(control).with_threshold(2)
5069
        end
5070 5071 5072
      end
    end
  end
R
Rob Watson 已提交
5073

5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092
  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

R
Rob Watson 已提交
5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136
  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
5137

5138
  describe '#toggle_ci_cd_settings!' do
5139
    it 'toggles the value on #settings' do
5140
      project = create(:project, group_runners_enabled: false)
5141 5142 5143

      expect(project.group_runners_enabled).to be false

5144
      project.toggle_ci_cd_settings!(:group_runners_enabled)
5145 5146 5147 5148

      expect(project.group_runners_enabled).to be true
    end
  end
5149

5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166
  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]) }
5167

5168 5169 5170
      it { is_expected.to be_nil }
    end

5171
    context 'when there is a gitlab deploy token associated but it is expired' do
5172 5173 5174 5175
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) }

      it { is_expected.to be_nil }
    end
5176

5177
    context 'when there is a deploy token associated with a different name' do
5178 5179 5180 5181
      let!(:deploy_token) { create(:deploy_token, projects: [project]) }

      it { is_expected.to be_nil }
    end
5182 5183 5184 5185 5186 5187 5188

    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
5189
  end
J
Jan Provaznik 已提交
5190 5191

  context 'with uploads' do
5192
    it_behaves_like 'model with uploads', true do
J
Jan Provaznik 已提交
5193 5194 5195 5196 5197
      let(:model_object) { create(:project, :with_avatar) }
      let(:upload_attribute) { :avatar }
      let(:uploader_class) { AttachmentUploader }
    end
  end
5198

5199
  describe '#members_among' do
5200
    let(:users) { create_list(:user, 3) }
5201

5202 5203
    let_it_be(:group) { create(:group) }
    let_it_be(:project) { create(:project, namespace: group) }
5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240

    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
5241
  end
5242

5243 5244
  describe "#find_or_initialize_services" do
    subject { build(:project) }
5245

5246 5247 5248
    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))
5249

5250
      services = subject.find_or_initialize_services
5251

5252 5253 5254 5255
      expect(services.count).to eq 1
      expect(services).to include(PushoverService)
    end
  end
5256

5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273
  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
5274
    end
5275 5276
  end

5277 5278 5279 5280 5281 5282 5283 5284 5285
  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

S
Shinya Maeda 已提交
5286 5287 5288
  describe '.deployments' do
    subject { project.deployments }

5289
    let(:project) { create(:project, :repository) }
S
Shinya Maeda 已提交
5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319

    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

5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341
  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

T
Thong Kuah 已提交
5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360
  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
5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371

    context 'project is hosted on instance with integrated cluster' do
      let(:group_cluster) { create(:cluster, :group) }
      let(:instance_cluster) { create(:cluster, :instance) }
      let(:group) { group_cluster.group }
      let(:project) { create(:project, group: group) }

      it 'returns all available clusters for this project' do
        expect(subject).to contain_exactly(cluster, group_cluster, instance_cluster)
      end
    end
T
Thong Kuah 已提交
5372 5373
  end

5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388
  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
5389 5390

        expect(project.reload.pool_repository).to eq(subject[:pool_repository])
5391 5392 5393 5394
      end
    end
  end

5395 5396
  describe '#git_objects_poolable?' do
    subject { project }
5397

5398 5399
    context 'when not using hashed storage' do
      let(:project) { create(:project, :legacy_storage, :public, :repository) }
5400 5401 5402 5403

      it { is_expected.not_to be_git_objects_poolable }
    end

5404
    context 'when the project is private' do
5405
      let(:project) { create(:project, :private) }
5406

5407 5408
      it { is_expected.not_to be_git_objects_poolable }
    end
5409

5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421
    context 'when the project is public' do
      let(:project) { create(:project, :repository, :public) }

      it { is_expected.to be_git_objects_poolable }
    end

    context 'when the project is internal' do
      let(:project) { create(:project, :repository, :internal) }

      it { is_expected.to be_git_objects_poolable }
    end

5422 5423
    context 'when objects are poolable' do
      let(:project) { create(:project, :repository, :public) }
5424

5425
      it { is_expected.to be_git_objects_poolable }
5426 5427 5428
    end
  end

5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439
  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

5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508
  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

5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537
  context 'pages deployed' do
    let(:project) { create(:project) }

    {
      mark_pages_as_deployed: true,
      mark_pages_as_not_deployed: false
    }.each do |method_name, flag|
      describe method_name do
        it "creates new record and sets deployed to #{flag} if none exists yet" do
          project.pages_metadatum.destroy!
          project.reload

          project.send(method_name)

          expect(project.pages_metadatum.reload.deployed).to eq(flag)
        end

        it "updates the existing record and sets deployed to #{flag}" do
          pages_metadatum = project.pages_metadatum
          pages_metadatum.update!(deployed: !flag)

          expect { project.send(method_name) }.to change {
            pages_metadatum.reload.deployed
          }.from(!flag).to(flag)
        end
      end
    end
  end

5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552
  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

5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572
  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

5573 5574 5575 5576 5577
  describe '#pages_lookup_path' do
    let(:pages_domain) { build(:pages_domain) }
    let(:project) { build(:project) }

    it 'returns instance of Pages::LookupPath' do
5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593
      expect(Pages::LookupPath).to receive(:new).with(project, domain: pages_domain, trim_prefix: 'mygroup').and_call_original

      expect(project.pages_lookup_path(domain: pages_domain, trim_prefix: 'mygroup')).to be_a(Pages::LookupPath)
    end
  end

  describe '.with_pages_deployed' do
    it 'returns only projects that have pages deployed' do
      _project_without_pages = create(:project)
      project_with_pages = create(:project)
      project_with_pages.mark_pages_as_deployed

      expect(described_class.with_pages_deployed).to contain_exactly(project_with_pages)
    end
  end

5594 5595 5596 5597 5598 5599 5600 5601 5602 5603
  describe '.pages_metadata_not_migrated' do
    it 'returns only projects that have pages deployed' do
      _project_with_pages_metadata_migrated = create(:project)
      project_with_pages_metadata_not_migrated = create(:project)
      project_with_pages_metadata_not_migrated.pages_metadatum.destroy!

      expect(described_class.pages_metadata_not_migrated).to contain_exactly(project_with_pages_metadata_not_migrated)
    end
  end

5604 5605 5606 5607 5608 5609 5610 5611 5612 5613
  describe '#pages_group_root?' do
    it 'returns returns true if pages_url is same as pages_group_url' do
      project = build(:project)
      expect(project).to receive(:pages_url).and_return(project.pages_group_url)

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

    it 'returns returns false if pages_url is different than pages_group_url' do
      project = build(:project)
5614

5615
      expect(project.pages_group_root?).to eq(false)
5616 5617 5618
    end
  end

5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665
  describe '#closest_setting' do
    using RSpec::Parameterized::TableSyntax

    shared_examples_for 'fetching closest setting' do
      let!(:namespace) { create(:namespace) }
      let!(:project) { create(:project, namespace: namespace) }

      let(:setting_name) { :some_setting }
      let(:setting) { project.closest_setting(setting_name) }

      before do
        allow(project).to receive(:read_attribute).with(setting_name).and_return(project_setting)
        allow(namespace).to receive(:closest_setting).with(setting_name).and_return(group_setting)
        allow(Gitlab::CurrentSettings).to receive(setting_name).and_return(global_setting)
      end

      it 'returns closest non-nil value' do
        expect(setting).to eq(result)
      end
    end

    context 'when setting is of non-boolean type' do
      where(:global_setting, :group_setting, :project_setting, :result) do
        100 | 200 | 300 | 300
        100 | 200 | nil | 200
        100 | nil | nil | 100
        nil | nil | nil | nil
      end

      with_them do
        it_behaves_like 'fetching closest setting'
      end
    end

    context 'when setting is of boolean type' do
      where(:global_setting, :group_setting, :project_setting, :result) do
        true | true  | false | false
        true | false | nil   | false
        true | nil   | nil   | true
      end

      with_them do
        it_behaves_like 'fetching closest setting'
      end
    end
  end

5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720
  describe '#drop_visibility_level!' do
    context 'when has a group' do
      let(:group) { create(:group, visibility_level: group_visibility_level) }
      let(:project) { build(:project, namespace: group, visibility_level: project_visibility_level) }

      context 'when the group `visibility_level` is more strict' do
        let(:group_visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
        let(:project_visibility_level) { Gitlab::VisibilityLevel::INTERNAL }

        it 'sets `visibility_level` value from the group' do
          expect { project.drop_visibility_level! }
            .to change { project.visibility_level }
            .to(Gitlab::VisibilityLevel::PRIVATE)
        end
      end

      context 'when the group `visibility_level` is less strict' do
        let(:group_visibility_level) { Gitlab::VisibilityLevel::INTERNAL }
        let(:project_visibility_level) { Gitlab::VisibilityLevel::PRIVATE }

        it 'does not change the value of the `visibility_level` field' do
          expect { project.drop_visibility_level! }
            .not_to change { project.visibility_level }
        end
      end
    end

    context 'when `restricted_visibility_levels` of the GitLab instance exist' do
      before do
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
      end

      let(:project) { build(:project, visibility_level: project_visibility_level) }

      context 'when `visibility_level` is included into `restricted_visibility_levels`' do
        let(:project_visibility_level) { Gitlab::VisibilityLevel::INTERNAL }

        it 'sets `visibility_level` value to `PRIVATE`' do
          expect { project.drop_visibility_level! }
            .to change { project.visibility_level }
            .to(Gitlab::VisibilityLevel::PRIVATE)
        end
      end

      context 'when `restricted_visibility_levels` does not include `visibility_level`' do
        let(:project_visibility_level) { Gitlab::VisibilityLevel::PUBLIC }

        it 'does not change the value of the `visibility_level` field' do
          expect { project.drop_visibility_level! }
            .to not_change { project.visibility_level }
        end
      end
    end
  end

5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734
  describe 'with services and chat names' do
    subject { create(:project) }

    let(:service) { create(:service, project: subject) }

    before do
      create_list(:chat_name, 5, service: service)
    end

    it 'removes chat names on removal' do
      expect { subject.destroy }.to change { ChatName.count }.by(-5)
    end
  end

5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753
  describe 'with_issues_or_mrs_available_for_user' do
    before do
      Project.delete_all
    end

    it 'returns correct projects' do
      user = create(:user)
      project1 = create(:project, :public, :merge_requests_disabled, :issues_enabled)
      project2 = create(:project, :public, :merge_requests_disabled, :issues_disabled)
      project3 = create(:project, :public, :issues_enabled, :merge_requests_enabled)
      project4 = create(:project, :private, :issues_private, :merge_requests_private)

      [project1, project2, project3, project4].each { |project| project.add_developer(user) }

      expect(described_class.with_issues_or_mrs_available_for_user(user))
        .to contain_exactly(project1, project3, project4)
    end
  end

5754 5755 5756 5757 5758 5759 5760 5761
  describe '#limited_protected_branches' do
    let(:project) { create(:project) }
    let!(:protected_branch) { create(:protected_branch, project: project) }
    let!(:another_protected_branch) { create(:protected_branch, project: project) }

    subject { project.limited_protected_branches(1) }

    it 'returns limited number of protected branches based on specified limit' do
5762
      expect(subject.count).to eq(1)
5763 5764 5765
    end
  end

5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812
  describe '#all_lfs_objects_oids' do
    let(:project) { create(:project) }
    let(:lfs_object) { create(:lfs_object) }
    let(:another_lfs_object) { create(:lfs_object) }

    subject { project.all_lfs_objects_oids }

    context 'when project has associated LFS objects' do
      before do
        create(:lfs_objects_project, lfs_object: lfs_object, project: project)
        create(:lfs_objects_project, lfs_object: another_lfs_object, project: project)
      end

      it 'returns OIDs of LFS objects' do
        expect(subject).to match_array([lfs_object.oid, another_lfs_object.oid])
      end

      context 'and there are specified oids' do
        subject { project.all_lfs_objects_oids(oids: [lfs_object.oid]) }

        it 'returns OIDs of LFS objects that match specified oids' do
          expect(subject).to eq([lfs_object.oid])
        end
      end
    end

    context 'when fork has associated LFS objects to itself and source' do
      let(:source) { create(:project) }
      let(:project) { fork_project(source) }

      before do
        create(:lfs_objects_project, lfs_object: lfs_object, project: source)
        create(:lfs_objects_project, lfs_object: another_lfs_object, project: project)
      end

      it 'returns OIDs of LFS objects' do
        expect(subject).to match_array([lfs_object.oid, another_lfs_object.oid])
      end
    end

    context 'when project has no associated LFS objects' do
      it 'returns empty array' do
        expect(subject).to be_empty
      end
    end
  end

5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828
  describe '#lfs_objects_oids' do
    let(:project) { create(:project) }
    let(:lfs_object) { create(:lfs_object) }
    let(:another_lfs_object) { create(:lfs_object) }

    subject { project.lfs_objects_oids }

    context 'when project has associated LFS objects' do
      before do
        create(:lfs_objects_project, lfs_object: lfs_object, project: project)
        create(:lfs_objects_project, lfs_object: another_lfs_object, project: project)
      end

      it 'returns OIDs of LFS objects' do
        expect(subject).to match_array([lfs_object.oid, another_lfs_object.oid])
      end
5829 5830 5831 5832 5833 5834 5835 5836

      context 'and there are specified oids' do
        subject { project.lfs_objects_oids(oids: [lfs_object.oid]) }

        it 'returns OIDs of LFS objects that match specified oids' do
          expect(subject).to eq([lfs_object.oid])
        end
      end
5837 5838 5839 5840 5841 5842 5843 5844 5845
    end

    context 'when project has no associated LFS objects' do
      it 'returns empty array' do
        expect(subject).to be_empty
      end
    end
  end

5846 5847 5848 5849 5850
  describe '#alerts_service_activated?' do
    let!(:project) { create(:project) }

    subject { project.alerts_service_activated? }

5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865
    context 'when project has an activated alerts service' do
      before do
        create(:alerts_service, project: project)
      end

      it { is_expected.to be_truthy }
    end

    context 'when project has an inactive alerts service' do
      before do
        create(:alerts_service, :inactive, project: project)
      end

      it { is_expected.to be_falsey }
    end
5866 5867
  end

5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885
  describe '#self_monitoring?' do
    let_it_be(:project) { create(:project) }

    subject { project.self_monitoring? }

    context 'when the project is instance self monitoring' do
      before do
        stub_application_setting(self_monitoring_project_id: project.id)
      end

      it { is_expected.to be true }
    end

    context 'when the project is not self monitoring' do
      it { is_expected.to be false }
    end
  end

5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960
  describe '#add_export_job' do
    context 'if not already present' do
      it 'starts project export job' do
        user = create(:user)
        project = build(:project)

        expect(ProjectExportWorker).to receive(:perform_async).with(user.id, project.id, nil, {})

        project.add_export_job(current_user: user)
      end
    end
  end

  describe '#export_in_progress?' do
    let(:project) { build(:project) }
    let!(:project_export_job ) { create(:project_export_job, project: project) }

    context 'when project export is enqueued' do
      it { expect(project.export_in_progress?).to be false }
    end

    context 'when project export is in progress' do
      before do
        project_export_job.start!
      end

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

    context 'when project export is completed' do
      before do
        finish_job(project_export_job)
      end

      it { expect(project.export_in_progress?).to be false }
    end
  end

  describe '#export_status' do
    let(:project) { build(:project) }
    let!(:project_export_job ) { create(:project_export_job, project: project) }

    context 'when project export is enqueued' do
      it { expect(project.export_status).to eq :queued }
    end

    context 'when project export is in progress' do
      before do
        project_export_job.start!
      end

      it { expect(project.export_status).to eq :started }
    end

    context 'when project export is completed' do
      before do
        finish_job(project_export_job)
        allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
      end

      it { expect(project.export_status).to eq :finished }
    end

    context 'when project export is being regenerated' do
      let!(:new_project_export_job ) { create(:project_export_job, project: project) }

      before do
        finish_job(project_export_job)
        allow(project).to receive(:export_file).and_return(double(ImportExportUploader, file: 'exists.zip'))
      end

      it { expect(project.export_status).to eq :regeneration_in_progress }
    end
  end

5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978
  describe '#environments_for_scope' do
    let_it_be(:project, reload: true) { create(:project) }

    before do
      create_list(:environment, 2, project: project)
    end

    it 'retrieves all project environments when using the * wildcard' do
      expect(project.environments_for_scope("*")).to eq(project.environments)
    end

    it 'retrieves a specific project environment when using the name of that environment' do
      environment = project.environments.first

      expect(project.environments_for_scope(environment.name)).to eq([environment])
    end
  end

5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006
  describe '#latest_jira_import' do
    let_it_be(:project) { create(:project) }
    context 'when no jira imports' do
      it 'returns nil' do
        expect(project.latest_jira_import).to be nil
      end
    end

    context 'when single jira import' do
      let!(:jira_import1) { create(:jira_import_state, project: project) }

      it 'returns the jira import' do
        expect(project.latest_jira_import).to eq(jira_import1)
      end
    end

    context 'when multiple jira imports' do
      let!(:jira_import1) { create(:jira_import_state, :finished, created_at: 1.day.ago, project: project) }
      let!(:jira_import2) { create(:jira_import_state, :failed, created_at: 2.days.ago, project: project) }
      let!(:jira_import3) { create(:jira_import_state, :started, created_at: 3.days.ago, project: project) }

      it 'returns latest jira import by created_at' do
        expect(project.jira_imports.pluck(:id)).to eq([jira_import3.id, jira_import2.id, jira_import1.id])
        expect(project.latest_jira_import).to eq(jira_import1)
      end
    end
  end

6007 6008 6009 6010 6011
  def finish_job(export_job)
    export_job.start
    export_job.finish
  end

6012
  def rugged_config
6013
    rugged_repo(project.repository).config
6014
  end
6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028

  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
G
gitlabhq 已提交
6029
end