project_spec.rb 147.3 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

S
Shinya Maeda 已提交
9
  it_behaves_like 'having unique enum values'
S
Shinya Maeda 已提交
10

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

100 101 102 103
    it 'has an inverse relationship with merge requests' do
      expect(described_class.reflect_on_association(:merge_requests).has_inverse?).to eq(:target_project)
    end

F
Felipe Artur 已提交
104 105
    context 'after initialized' do
      it "has a project_feature" do
106
        expect(described_class.new.project_feature).to be_present
107 108 109
      end
    end

110 111 112 113 114 115 116 117 118
    context 'when creating a new project' do
      it 'automatically creates a CI/CD settings row' do
        project = create(:project)

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

119 120 121 122
    context 'updating cd_cd_settings' do
      it 'does not raise an error' do
        project = create(:project)

J
James Lopez 已提交
123
        expect { project.update(ci_cd_settings: nil) }.not_to raise_exception
124 125 126
      end
    end

127
    describe '#members & #requesters' do
128
      let(:project) { create(:project, :public, :access_requestable) }
129 130 131 132
      let(:requester) { create(:user) }
      let(:developer) { create(:user) }
      before do
        project.request_access(requester)
133
        project.add_developer(developer)
134 135
      end

136 137
      it_behaves_like 'members and requesters associations' do
        let(:namespace) { project }
138 139
      end
    end
140

141
    describe 'ci_pipelines association' do
142 143
      it 'returns only pipelines from ci_sources' do
        expect(Ci::Pipeline).to receive(:ci_sources).and_call_original
144

145
        subject.ci_pipelines
146 147
      end
    end
G
gitlabhq 已提交
148 149
  end

150 151 152 153 154 155 156 157
  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) }
158 159
  end

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
  describe '.missing_kubernetes_namespace' do
    let!(:project) { create(:project) }
    let!(:cluster) { create(:cluster, :provided_by_user, :group) }
    let(:kubernetes_namespaces) { project.kubernetes_namespaces }

    subject { described_class.missing_kubernetes_namespace(kubernetes_namespaces) }

    it { is_expected.to contain_exactly(project) }

    context 'kubernetes namespace exists' do
      before do
        create(:cluster_kubernetes_namespace, project: project, cluster: cluster)
      end

      it { is_expected.to be_empty }
    end
  end

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

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

195 196 197 198 199 200 201 202
    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)
        .with_message('needs to be beetween 10 minutes and 1 month')
    end

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

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

212
      expect(project2).not_to be_valid
213
    end
214 215 216

    describe 'wiki path conflict' do
      context "when the new path has been used by the wiki of other Project" do
217
        it 'has an error on the name attribute' do
218
          new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
219 220 221 222 223 224 225

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

      context "when the new wiki path has been used by the path of other Project" do
226
        it 'has an error on the name attribute' do
227 228
          project_with_wiki_suffix = create(:project, path: 'foo.wiki')
          new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
229 230 231 232 233 234

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

236
    context 'repository storages inclusion' do
237
      let(:project2) { build(:project, repository_storage: 'missing') }
238 239

      before do
240
        storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } }
241 242 243
        allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
      end

244
      it "does not allow repository storages that don't match a label in the configuration" do
245 246 247 248
        expect(project2).not_to be_valid
        expect(project2.errors[:repository_storage].first).to match(/is not included in the list/)
      end
    end
249

250 251 252
    describe 'import_url' do
      it 'does not allow an invalid URI as import_url' do
        project = build(:project, import_url: 'invalid://')
J
James Lopez 已提交
253

254 255
        expect(project).not_to be_valid
      end
256

257 258 259
      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'
260

261 262
        expect(project).to be_valid
      end
263

264 265
      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')
266

267 268
        expect(project).not_to be_valid
      end
J
James Lopez 已提交
269

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

273 274
        expect(project).to be_valid
      end
275

276 277
      it 'allows an empty URI' do
        project = build(:project, import_url: '')
278

279 280
        expect(project).to be_valid
      end
281

282 283
      it 'does not produce import data on an empty URI' do
        project = build(:project, import_url: '')
284

285 286
        expect(project.import_data).to be_nil
      end
287

288 289
      it 'does not produce import data on an invalid URI' do
        project = build(:project, import_url: 'test://')
290

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

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

297 298 299
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
      end
300

301 302 303 304 305 306 307
      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

308 309
      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 已提交
310

311 312 313
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 80, 443')
      end
D
Douwe Maan 已提交
314

315 316 317
      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 已提交
318

319 320 321
        expect(project).to be_invalid
        expect(project.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
      end
D
Douwe Maan 已提交
322

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

326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        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
344 345
    end

346 347
    describe 'project pending deletion' do
      let!(:project_pending_deletion) do
348
        create(:project,
349 350 351
               pending_delete: true)
      end
      let(:new_project) do
352
        build(:project,
353 354 355 356 357 358 359 360 361 362 363 364
              name: project_pending_deletion.name,
              namespace: project_pending_deletion.namespace)
      end

      before do
        new_project.validate
      end

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

    describe 'path validation' do
      it 'allows paths reserved on the root namespace' do
368
        project = build(:project, path: 'api')
369 370 371 372 373

        expect(project).to be_valid
      end

      it 'rejects paths reserved on another level' do
374
        project = build(:project, path: 'tree')
375 376 377

        expect(project).not_to be_valid
      end
378 379 380

      it 'rejects nested paths' do
        parent = create(:group, :nested, path: 'environments')
381
        project = build(:project, path: 'folders', namespace: parent)
382 383 384

        expect(project).not_to be_valid
      end
385 386 387

      it 'allows a reserved group name' do
        parent = create(:group)
388
        project = build(:project, path: 'avatar', namespace: parent)
389 390 391

        expect(project).to be_valid
      end
392 393 394 395 396 397

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

        expect(project).to be_valid
      end
398
    end
G
gitlabhq 已提交
399
  end
400

401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
  describe '#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

      it 'should return .external pipelines' do
        expect(project.all_pipelines).to all(have_attributes(source: 'external'))
        expect(project.all_pipelines.size).to eq(1)
      end
    end
  end

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

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

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

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

      it 'should return .external pipelines' do
        expect(project.ci_pipelines).to all(have_attributes(source: 'external'))
        expect(project.ci_pipelines.size).to eq(1)
      end
    end
  end

K
Kamil Trzcinski 已提交
449
  describe 'project token' do
450
    it 'sets an random token if none provided' do
451
      project = FactoryBot.create(:project, runners_token: '')
K
Kamil Trzcinski 已提交
452
      expect(project.runners_token).not_to eq('')
K
Kamil Trzcinski 已提交
453 454
    end

U
ubudzisz 已提交
455
    it 'does not set an random token if one provided' do
456
      project = FactoryBot.create(:project, runners_token: 'my-token')
K
Kamil Trzcinski 已提交
457
      expect(project.runners_token).to eq('my-token')
K
Kamil Trzcinski 已提交
458 459
    end
  end
G
gitlabhq 已提交
460

461
  describe 'Respond to' do
462 463 464 465 466
    it { is_expected.to respond_to(:url_to_repo) }
    it { is_expected.to respond_to(:repo_exists?) }
    it { is_expected.to respond_to(:execute_hooks) }
    it { is_expected.to respond_to(:owner) }
    it { is_expected.to respond_to(:path_with_namespace) }
467
    it { is_expected.to respond_to(:full_path) }
G
gitlabhq 已提交
468 469
  end

470
  describe 'delegation' do
471
    [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_user, :add_users].each do |method|
472 473 474 475 476
      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) }
477 478
    it { is_expected.to delegate_method(:group_clusters_enabled?).to(:group).with_arguments(allow_nil: true) }
    it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
479
    it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
480 481
  end

482 483 484 485 486 487 488 489 490
  describe '#to_reference_with_postfix' do
    it 'returns the full path with reference_postfix' do
      namespace = create(:namespace, path: 'sample-namespace')
      project = create(:project, path: 'sample-project', namespace: namespace)

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

491
  describe '#to_reference' do
492
    let(:owner)     { create(:user, name: 'Gitlab') }
493
    let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
494
    let(:project)   { create(:project, path: 'sample-project', namespace: namespace) }
495
    let(:group)     { create(:group, name: 'Group', path: 'sample-group') }
496

497
    context 'when nil argument' do
498 499 500 501 502
      it 'returns nil' do
        expect(project.to_reference).to be_nil
      end
    end

503
    context 'when full is true' do
504
      it 'returns complete path to the project' do
505 506 507
        expect(project.to_reference(full: true)).to          eq 'sample-namespace/sample-project'
        expect(project.to_reference(project, full: true)).to eq 'sample-namespace/sample-project'
        expect(project.to_reference(group, full: true)).to   eq 'sample-namespace/sample-project'
508 509 510 511 512 513 514 515 516 517
      end
    end

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

    context 'when cross namespace project argument' do
518
      let(:another_namespace_project) { create(:project, name: 'another-project') }
519 520 521 522 523 524 525

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

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

528
      it 'returns path to the project' do
529 530 531
        expect(project.to_reference(another_project)).to eq 'sample-project'
      end
    end
532

533 534
    context 'when different namespace / cross-project argument' do
      let(:another_namespace) { create(:namespace, path: 'another-namespace', owner: owner) }
535
      let(:another_project)   { create(:project, path: 'another-project', namespace: another_namespace) }
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552

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

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

      context 'with different project path' do
        it 'returns full path to the project' do
          expect(project.to_reference(group)).to eq 'sample-namespace/sample-project'
        end
553 554
      end
    end
555 556 557 558 559
  end

  describe '#to_human_reference' do
    let(:owner) { create(:user, name: 'Gitlab') }
    let(:namespace) { create(:namespace, name: 'Sample namespace', owner: owner) }
560
    let(:project) { create(:project, name: 'Sample project', namespace: namespace) }
561 562 563 564 565 566 567 568 569 570 571 572 573 574

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

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

    context 'when cross namespace project argument' do
575
      let(:another_namespace_project) { create(:project, name: 'another-project') }
576 577 578 579 580 581 582

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

    context 'when same namespace / cross-project argument' do
583
      let(:another_project) { create(:project, namespace: namespace) }
584 585 586 587

      it 'returns name of the project' do
        expect(project.to_human_reference(another_project)).to eq 'Sample project'
      end
588 589 590
    end
  end

V
Valery Sizov 已提交
591
  describe '#merge_method' do
592 593 594 595 596 597 598
    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 已提交
599 600
    end

601 602 603 604 605 606
    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 已提交
607 608 609
    end
  end

610
  it 'returns valid url to repo' do
611
    project = described_class.new(path: 'somewhere')
612
    expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git')
G
gitlabhq 已提交
613 614
  end

D
Douwe Maan 已提交
615
  describe "#web_url" do
616
    let(:project) { create(:project, path: "somewhere") }
D
Douwe Maan 已提交
617 618

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

I
Imre Farkas 已提交
623 624
  describe "#readme_url" do
    context 'with a non-existing repository' do
625
      let(:project) { create(:project) }
I
Imre Farkas 已提交
626

627
      it 'returns nil' do
I
Imre Farkas 已提交
628 629 630 631 632 633
        expect(project.readme_url).to be_nil
      end
    end

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

636
        it 'returns nil' do
I
Imre Farkas 已提交
637 638 639 640 641
          expect(project.readme_url).to be_nil
        end
      end

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

I
Imre Farkas 已提交
644
        it 'returns the README' do
645
          expect(project.readme_url).to eq("#{project.web_url}/blob/master/README.md")
I
Imre Farkas 已提交
646 647 648 649 650
        end
      end
    end
  end

J
Jan Provaznik 已提交
651
  describe "#new_issuable_address" do
652
    let(:project) { create(:project, path: "somewhere") }
653 654
    let(:user) { create(:user) }

655 656 657 658 659 660
    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
661
        address = "p+#{project.full_path_slug}-#{project.project_id}-#{user.incoming_email_token}-issue@gl.ab"
662

J
Jan Provaznik 已提交
663 664 665 666
        expect(project.new_issuable_address(user, 'issue')).to eq(address)
      end

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

        expect(project.new_issuable_address(user, 'merge_request')).to eq(address)
670
      end
671 672 673 674

      it 'returns nil with invalid address type' do
        expect(project.new_issuable_address(user, 'invalid_param')).to be_nil
      end
675 676 677 678 679 680
    end

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

682
      it 'returns nil' do
J
Jan Provaznik 已提交
683 684 685 686 687
        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
688
      end
689 690 691
    end
  end

692
  describe 'last_activity methods' do
S
Stan Hu 已提交
693 694
    let(:timestamp) { 2.hours.ago }
    # last_activity_at gets set to created_at upon creation
695
    let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
G
gitlabhq 已提交
696

697
    describe 'last_activity' do
698
      it 'alias last_activity to last_event' do
699
        last_event = create(:event, :closed, project: project)
700

701
        expect(project.last_activity).to eq(last_event)
702
      end
G
gitlabhq 已提交
703 704
    end

705 706
    describe 'last_activity_date' do
      it 'returns the creation date of the project\'s last event if present' do
707
        new_event = create(:event, :closed, project: project, created_at: Time.now)
708

S
Stan Hu 已提交
709
        project.reload
710
        expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
711
      end
712

713
      it 'returns the project\'s last update date if it has no events' do
714
        expect(project.last_activity_date).to eq(project.updated_at)
715
      end
716 717

      it 'returns the most recent timestamp' do
L
Lin Jen-Shin 已提交
718 719 720
        project.update(updated_at: nil,
                       last_activity_at: timestamp,
                       last_repository_updated_at: timestamp - 1.hour)
721

722
        expect(project.last_activity_date).to be_like_time(timestamp)
723

L
Lin Jen-Shin 已提交
724 725 726
        project.update(updated_at: timestamp,
                       last_activity_at: timestamp - 1.hour,
                       last_repository_updated_at: nil)
727

728
        expect(project.last_activity_date).to be_like_time(timestamp)
729
      end
730 731
    end
  end
732

733
  describe '#get_issue' do
734
    let(:project) { create(:project) }
S
Stan Hu 已提交
735
    let!(:issue)  { create(:issue, project: project) }
736 737 738
    let(:user)    { create(:user) }

    before do
739
      project.add_developer(user)
740
    end
741 742 743

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

S
Stan Hu 已提交
747 748 749 750
      it 'returns count of open issues' do
        expect(project.open_issues_count).to eq(1)
      end

751
      it 'returns nil when no issue found' do
752 753 754 755 756 757
        expect(project.get_issue(999, user)).to be_nil
      end

      it "returns nil when user doesn't have access" do
        user = create(:user)
        expect(project.get_issue(issue.iid, user)).to eq nil
758 759 760 761
      end
    end

    context 'with external issues tracker' do
762
      let!(:internal_issue) { create(:issue, project: project) }
763
      before do
764
        allow(project).to receive(:external_issue_tracker).and_return(true)
765 766
      end

767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
      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
804 805 806 807 808
      end
    end
  end

  describe '#issue_exists?' do
809
    let(:project) { create(:project) }
810 811 812 813 814 815 816 817 818 819 820 821

    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 已提交
822
  describe '#to_param' do
823 824
    context 'with namespace' do
      before do
825
        @group = create(:group, name: 'gitlab')
826
        @project = create(:project, name: 'gitlabhq', namespace: @group)
827 828
      end

V
Vinnie Okada 已提交
829
      it { expect(@project.to_param).to eq('gitlabhq') }
830
    end
831 832 833

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

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

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

854
    it 'returns valid repo' do
855
      expect(project.repository).to be_kind_of(Repository)
D
Dmitriy Zaporozhets 已提交
856 857
    end
  end
858

L
Lin Jen-Shin 已提交
859
  describe '#default_issues_tracker?' do
860
    it "is true if used internal tracker" do
861
      project = build(:project)
862

863
      expect(project.default_issues_tracker?).to be_truthy
864 865
    end

866
    it "is false if used other tracker" do
867 868 869 870
      # NOTE: The current nature of this factory requires persistence
      project = create(:redmine_project)

      expect(project.default_issues_tracker?).to be_falsey
871 872 873
    end
  end

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
  describe '#empty_repo?' do
    context 'when the repo does not exist' do
      let(:project) { build_stubbed(:project) }

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

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

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

L
Lin Jen-Shin 已提交
892
  describe '#external_issue_tracker' do
893
    let(:project) { create(:project) }
894 895 896
    let(:ext_project) { create(:redmine_project) }

    context 'on existing projects with no value for has_external_issue_tracker' do
897
      before do
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
        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 已提交
927
  describe '#cache_has_external_issue_tracker' do
928
    let(:project) { create(:project, has_external_issue_tracker: nil) }
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946

    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 已提交
947 948 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 978 979 980 981 982 983 984

    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
985 986
  end

987
  describe '#has_wiki?' do
988 989 990
    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) }
991 992 993 994 995 996 997 998

    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

999
  describe '#external_wiki' do
1000
    let(:project) { create(:project) }
1001

1002 1003 1004 1005 1006
    context 'with an active external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: true)
        project.external_wiki
      end
1007

1008 1009 1010
      it 'sets :has_external_wiki as true' do
        expect(project.has_external_wiki).to be(true)
      end
1011

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

1015 1016 1017 1018
        project.services.external_wikis.first.destroy

        expect(project.has_external_wiki).to be(false)
      end
1019 1020
    end

1021 1022 1023 1024
    context 'with an inactive external wiki' do
      before do
        create(:service, project: project, type: 'ExternalWikiService', active: false)
      end
1025

1026 1027 1028
      it 'sets :has_external_wiki as false' do
        expect(project.has_external_wiki).to be(false)
      end
1029 1030
    end

1031 1032 1033 1034
    context 'with no external wiki' do
      before do
        project.external_wiki
      end
1035

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
      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
1047 1048 1049
    end
  end

1050 1051
  describe '#star_count' do
    it 'counts stars from multiple users' do
1052 1053
      user1 = create(:user)
      user2 = create(:user)
1054
      project = create(:project, :public)
C
Ciro Santilli 已提交
1055 1056

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

C
Ciro Santilli 已提交
1058
      user1.toggle_star(project)
1059 1060
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
1061
      user2.toggle_star(project)
1062 1063 1064
      project.reload
      expect(project.reload.star_count).to eq(2)

C
Ciro Santilli 已提交
1065
      user1.toggle_star(project)
1066 1067 1068
      project.reload
      expect(project.reload.star_count).to eq(1)

C
Ciro Santilli 已提交
1069
      user2.toggle_star(project)
1070 1071 1072 1073
      project.reload
      expect(project.reload.star_count).to eq(0)
    end

1074
    it 'counts stars on the right project' do
1075
      user = create(:user)
1076 1077
      project1 = create(:project, :public)
      project2 = create(:project, :public)
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104

      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 已提交
1105 1106
    end
  end
1107

L
Lin Jen-Shin 已提交
1108
  describe '#avatar_type' do
1109
    let(:project) { create(:project) }
1110

1111
    it 'is true if avatar is image' do
1112
      project.update_attribute(:avatar, 'uploads/avatar.png')
1113
      expect(project.avatar_type).to be_truthy
1114 1115
    end

1116
    it 'is false if avatar is html page' do
1117
      project.update_attribute(:avatar, 'uploads/avatar.html')
1118
      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'])
1119 1120
    end
  end
S
sue445 已提交
1121

L
Lin Jen-Shin 已提交
1122
  describe '#avatar_url' do
S
sue445 已提交
1123 1124
    subject { project.avatar_url }

1125
    let(:project) { create(:project) }
S
sue445 已提交
1126

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

1130
      it 'shows correct url' do
1131 1132
        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)
1133
      end
S
sue445 已提交
1134 1135
    end

1136
    context 'when avatar file in git' do
S
sue445 已提交
1137 1138 1139 1140
      before do
        allow(project).to receive(:avatar_in_git) { true }
      end

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

1143
      it { is_expected.to eq "http://#{Gitlab.config.gitlab.host}#{avatar_path}" }
S
sue445 已提交
1144
    end
1145 1146

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

1149
      it { is_expected.to eq nil }
1150
    end
S
sue445 已提交
1151
  end
1152

1153
  describe '#pipeline_for' do
1154
    let(:project) { create(:project, :repository) }
1155
    let!(:pipeline) { create_pipeline(project) }
K
Kamil Trzcinski 已提交
1156

1157 1158
    shared_examples 'giving the correct pipeline' do
      it { is_expected.to eq(pipeline) }
K
Kamil Trzcinski 已提交
1159

1160
      context 'return latest' do
1161
        let!(:pipeline2) { create_pipeline(project) }
K
Kamil Trzcinski 已提交
1162

1163
        it { is_expected.to eq(pipeline2) }
K
Kamil Trzcinski 已提交
1164
      end
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177
    end

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

      it_behaves_like 'giving the correct pipeline'
    end

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

      it_behaves_like 'giving the correct pipeline'
    end
1178
  end
1179

L
Lin Jen-Shin 已提交
1180
  describe '#builds_enabled' do
1181
    let(:project) { create(:project) }
1182

1183 1184 1185
    subject { project.builds_enabled }

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

1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
  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

1200
  describe '.with_shared_runners' do
1201
    subject { described_class.with_shared_runners }
1202 1203

    context 'when shared runners are enabled for project' do
1204
      let!(:project) { create(:project, shared_runners_enabled: true) }
1205 1206 1207 1208 1209 1210 1211

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

    context 'when shared runners are disabled for project' do
1212
      let!(:project) { create(:project, shared_runners_enabled: false) }
1213 1214 1215 1216 1217 1218 1219

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

1220
  describe '.cached_count', :use_clean_rails_memory_store_caching do
1221
    let(:group)     { create(:group, :public) }
1222 1223
    let!(:project1) { create(:project, :public, group: group) }
    let!(:project2) { create(:project, :public, group: group) }
1224 1225

    it 'returns total project count' do
1226
      expect(described_class).to receive(:count).once.and_call_original
1227 1228

      3.times do
1229
        expect(described_class.cached_count).to eq(2)
1230 1231 1232 1233
      end
    end
  end

Y
Yorick Peterse 已提交
1234
  describe '.trending' do
F
Felipe Artur 已提交
1235
    let(:group)    { create(:group, :public) }
1236 1237
    let(:project1) { create(:project, :public, group: group) }
    let(:project2) { create(:project, :public, group: group) }
Y
Yorick Peterse 已提交
1238 1239 1240 1241 1242 1243 1244 1245

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

      create(:note_on_commit, project: project2)

Y
Yorick Peterse 已提交
1246
      TrendingProject.refresh!
Y
Yorick Peterse 已提交
1247 1248
    end

Y
Yorick Peterse 已提交
1249
    subject { described_class.trending.to_a }
Y
Yorick Peterse 已提交
1250

Y
Yorick Peterse 已提交
1251 1252
    it 'sorts projects by the amount of notes in descending order' do
      expect(subject).to eq([project1, project2])
Y
Yorick Peterse 已提交
1253
    end
1254 1255 1256 1257 1258 1259 1260 1261

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

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

T
Toon Claes 已提交
1264 1265 1266 1267
  describe '.starred_by' do
    it 'returns only projects starred by the given user' do
      user1 = create(:user)
      user2 = create(:user)
1268 1269 1270
      project1 = create(:project)
      project2 = create(:project)
      create(:project)
T
Toon Claes 已提交
1271 1272 1273
      user1.toggle_star(project1)
      user2.toggle_star(project2)

1274
      expect(described_class.starred_by(user1)).to contain_exactly(project1)
T
Toon Claes 已提交
1275 1276 1277
    end
  end

Y
Yorick Peterse 已提交
1278
  describe '.visible_to_user' do
1279
    let!(:project) { create(:project, :private) }
Y
Yorick Peterse 已提交
1280 1281 1282 1283 1284 1285
    let!(:user)    { create(:user) }

    subject { described_class.visible_to_user(user) }

    describe 'when a user has access to a project' do
      before do
1286
        project.add_user(user, Gitlab::Access::MAINTAINER)
Y
Yorick Peterse 已提交
1287 1288 1289 1290 1291 1292 1293 1294 1295
      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 已提交
1296

1297
  context 'repository storage by default' do
1298
    let(:project) { build(:project) }
1299 1300

    before do
1301
      storages = {
1302 1303
        'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'),
        'picked'  => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories')
1304
      }
1305 1306 1307
      allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
    end

1308 1309 1310 1311 1312
    it 'picks storage from ApplicationSetting' do
      expect_any_instance_of(ApplicationSetting).to receive(:pick_repository_storage).and_return('picked')

      expect(project.repository_storage).to eq('picked')
    end
1313 1314
  end

K
Kamil Trzcinski 已提交
1315
  context 'shared runners by default' do
1316
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
1317 1318 1319 1320

    subject { project.shared_runners_enabled }

    context 'are enabled' do
1321 1322 1323
      before do
        stub_application_setting(shared_runners_enabled: true)
      end
K
Kamil Trzcinski 已提交
1324 1325 1326 1327 1328

      it { is_expected.to be_truthy }
    end

    context 'are disabled' do
1329 1330 1331
      before do
        stub_application_setting(shared_runners_enabled: false)
      end
K
Kamil Trzcinski 已提交
1332 1333 1334 1335 1336

      it { is_expected.to be_falsey }
    end
  end

1337
  describe '#any_runners?' do
1338
    context 'shared runners' do
1339
      let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) }
1340 1341
      let(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
      let(:shared_runner) { create(:ci_runner, :instance) }
K
Kamil Trzcinski 已提交
1342

1343 1344
      context 'for shared runners disabled' do
        let(:shared_runners_enabled) { false }
1345

1346 1347 1348
        it 'has no runners available' do
          expect(project.any_runners?).to be_falsey
        end
1349

1350
        it 'has a specific runner' do
1351
          specific_runner
1352

1353 1354 1355 1356 1357
          expect(project.any_runners?).to be_truthy
        end

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

1359 1360
          expect(project.any_runners?).to be_falsey
        end
1361

1362
        it 'checks the presence of specific runner' do
1363
          specific_runner
1364

1365 1366
          expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy
        end
1367 1368

        it 'returns false if match cannot be found' do
1369
          specific_runner
1370

1371 1372
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1373
      end
1374

1375 1376 1377 1378 1379
      context 'for shared runners enabled' do
        let(:shared_runners_enabled) { true }

        it 'has a shared runner' do
          shared_runner
1380

1381 1382 1383 1384 1385
          expect(project.any_runners?).to be_truthy
        end

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

1387 1388
          expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy
        end
1389 1390 1391

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

1393 1394
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1395 1396
      end
    end
1397

1398
    context 'group runners' do
1399 1400
      let(:project) { create(:project, group_runners_enabled: group_runners_enabled) }
      let(:group) { create(:group, projects: [project]) }
1401
      let(:group_runner) { create(:ci_runner, :group, groups: [group]) }
1402 1403 1404

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

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

1413 1414
          expect(project.any_runners?).to be_falsey
        end
K
Kamil Trzcinski 已提交
1415 1416
      end

1417 1418 1419 1420 1421
      context 'for group runners enabled' do
        let(:group_runners_enabled) { true }

        it 'has a group runner' do
          group_runner
1422

1423 1424 1425 1426 1427
          expect(project.any_runners?).to be_truthy
        end

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

1429 1430
          expect(project.any_runners? { |runner| runner == group_runner }).to be_truthy
        end
1431 1432 1433

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

1435 1436
          expect(project.any_runners? { false }).to be_falsey
        end
K
Kamil Trzcinski 已提交
1437 1438 1439
      end
    end
  end
1440

1441
  describe '#shared_runners' do
1442
    let!(:runner) { create(:ci_runner, :instance) }
1443 1444 1445 1446

    subject { project.shared_runners }

    context 'when shared runners are enabled for project' do
1447
      let!(:project) { create(:project, shared_runners_enabled: true) }
1448 1449 1450 1451 1452 1453 1454

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

    context 'when shared runners are disabled for project' do
1455
      let!(:project) { create(:project, shared_runners_enabled: false) }
1456 1457 1458 1459 1460 1461 1462

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

1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480
  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
      before do
        stub_application_setting(default_project_visibility: Gitlab::VisibilityLevel::INTERNAL)
      end

      it { is_expected.to eq(Gitlab::VisibilityLevel::INTERNAL) }
    end
  end

1481
  describe '#visibility_level_allowed?' do
1482
    let(:project) { create(:project, :internal) }
1483 1484 1485 1486 1487 1488 1489 1490

    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
1491
      let(:project)        { create(:project, :internal) }
1492
      let(:forked_project) { fork_project(project) }
1493 1494 1495 1496 1497

      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
1498
  end
1499

1500
  describe '#pages_deployed?' do
1501
    let(:project) { create(:project) }
1502 1503 1504 1505

    subject { project.pages_deployed? }

    context 'if public folder does exist' do
1506 1507 1508
      before do
        allow(Dir).to receive(:exist?).with(project.public_pages_path).and_return(true)
      end
1509 1510 1511 1512 1513 1514 1515 1516 1517

      it { is_expected.to be_truthy }
    end

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

1518
  describe '#pages_url' do
1519 1520
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541
    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") }
1542 1543 1544 1545
    end
  end

  describe '#pages_group_url' do
1546 1547
    let(:group) { create(:group, name: group_name) }
    let(:project) { create(:project, namespace: group, name: project_name) }
1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569
    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}") }
1570 1571 1572
    end
  end

1573
  describe '.search' do
1574
    let(:project) { create(:project, description: 'kitten mittens') }
1575

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

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

1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
    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

1612
    describe 'with pending_delete project' do
1613
      let(:pending_delete_project) { create(:project, pending_delete: true) }
1614 1615 1616 1617 1618 1619 1620

      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
1621
  end
1622

Y
Yorick Peterse 已提交
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669
  describe '.optionally_search' do
    let(:project) { create(:project) }

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

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

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

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

  describe '.paginate_in_descending_order_using_id' do
    let!(:project1) { create(:project) }
    let!(:project2) { create(:project) }

    it 'orders the relation in descending order' do
      expect(described_class.paginate_in_descending_order_using_id)
        .to eq([project2, project1])
    end

    it 'applies a limit to the relation' do
      expect(described_class.paginate_in_descending_order_using_id(limit: 1))
        .to eq([project2])
    end

    it 'limits projects by and ID when given' do
      expect(described_class.paginate_in_descending_order_using_id(before: project2.id))
        .to eq([project1])
    end
  end

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

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

      expect(recorder.count).to be_zero
    end
  end

1670
  describe '#expire_caches_before_rename' do
1671
    let(:project) { create(:project, :repository) }
1672 1673 1674 1675
    let(:repo)    { double(:repo, exists?: true) }
    let(:wiki)    { double(:wiki, exists?: true) }

    it 'expires the caches of the repository and wiki' do
1676 1677 1678
      allow(Repository).to receive(:new)
        .with('foo', project)
        .and_return(repo)
1679

1680 1681 1682
      allow(Repository).to receive(:new)
        .with('foo.wiki', project)
        .and_return(wiki)
1683

1684 1685
      expect(repo).to receive(:before_delete)
      expect(wiki).to receive(:before_delete)
1686 1687 1688 1689

      project.expire_caches_before_rename('foo')
    end
  end
1690 1691

  describe '.search_by_title' do
1692
    let(:project) { create(:project, name: 'kittens') }
1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705

    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
1706 1707 1708 1709 1710

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

1711 1712
    let(:private_project)  { create(:project, :private, group: private_group) }
    let(:internal_project) { create(:project, :internal, group: internal_group) }
1713 1714 1715 1716 1717 1718 1719 1720 1721

    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
1722

1723
  describe '#track_project_repository' do
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738
    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
1739

1740 1741 1742 1743 1744 1745 1746
      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
1747

1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
        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
1760 1761
    end

1762 1763
    context 'with projects on legacy storage' do
      let(:project) { create(:project, :repository, :legacy_storage) }
1764

1765 1766
      it_behaves_like 'tracks storage location'
    end
1767

1768 1769
    context 'with projects on hashed storage' do
      let(:project) { create(:project, :repository) }
1770

1771
      it_behaves_like 'tracks storage location'
1772 1773 1774
    end
  end

1775
  describe '#create_repository' do
1776
    let(:project) { create(:project, :repository) }
1777 1778 1779 1780 1781 1782 1783 1784
    let(:shell) { Gitlab::Shell.new }

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

    context 'using a regular repository' do
      it 'creates the repository' do
1785
        expect(shell).to receive(:create_repository)
1786
          .with(project.repository_storage, project.disk_path, project.full_path)
1787
          .and_return(true)
1788 1789 1790 1791 1792 1793 1794

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

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

      it 'adds an error if the repository could not be created' do
1795
        expect(shell).to receive(:create_repository)
1796
          .with(project.repository_storage, project.disk_path, project.full_path)
1797
          .and_return(false)
1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808

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

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

    context 'using a forked repository' do
      it 'does nothing' do
        expect(project).to receive(:forked?).and_return(true)
1809
        expect(shell).not_to receive(:create_repository)
1810 1811 1812 1813 1814

        project.create_repository
      end
    end
  end
1815

1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827
  describe '#ensure_repository' do
    let(:project) { create(:project, :repository) }
    let(:shell) { Gitlab::Shell.new }

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

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

1828
      allow(shell).to receive(:create_repository)
1829
        .with(project.repository_storage, project.disk_path, project.full_path)
1830 1831
        .and_return(true)

1832
      expect(project).to receive(:create_repository).with(force: true)
1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844

      project.ensure_repository
    end

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

      expect(project).not_to receive(:create_repository)

      project.ensure_repository
    end
1845 1846 1847 1848 1849 1850 1851

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

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

1852
      expect(shell).to receive(:create_repository)
1853
        .with(project.repository_storage, project.disk_path, project.full_path)
1854 1855 1856 1857
        .and_return(true)

      project.ensure_repository
    end
1858 1859
  end

1860 1861 1862 1863 1864 1865 1866 1867 1868 1869
  describe 'handling import URL' do
    it 'returns the sanitized URL' do
      project = create(:project, :import_started, import_url: 'http://user:pass@test.com')

      project.import_state.finish

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

A
Andre Guedes 已提交
1870
  describe '#container_registry_url' do
1871
    let(:project) { create(:project) }
K
Kamil Trzcinski 已提交
1872

A
Andre Guedes 已提交
1873
    subject { project.container_registry_url }
K
Kamil Trzcinski 已提交
1874

1875 1876 1877
    before do
      stub_container_registry_config(**registry_settings)
    end
K
Kamil Trzcinski 已提交
1878 1879 1880

    context 'for enabled registry' do
      let(:registry_settings) do
1881 1882
        { enabled: true,
          host_port: 'example.com' }
K
Kamil Trzcinski 已提交
1883 1884
      end

1885
      it { is_expected.not_to be_nil }
K
Kamil Trzcinski 已提交
1886 1887 1888 1889
    end

    context 'for disabled registry' do
      let(:registry_settings) do
1890
        { enabled: false }
K
Kamil Trzcinski 已提交
1891 1892 1893 1894 1895 1896
      end

      it { is_expected.to be_nil }
    end
  end

1897
  describe '#has_container_registry_tags?' do
1898
    let(:project) { create(:project) }
1899 1900

    context 'when container registry is enabled' do
1901 1902 1903
      before do
        stub_container_registry_config(enabled: true)
      end
1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940

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

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

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

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

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

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

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

    context 'when container registry is disabled' do
1941 1942 1943
      before do
        stub_container_registry_config(enabled: false)
      end
1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960

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

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

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

1961
  describe '#ci_config_path=' do
1962
    let(:project) { create(:project) }
1963 1964

    it 'sets nil' do
1965
      project.update!(ci_config_path: nil)
1966

1967
      expect(project.ci_config_path).to be_nil
1968 1969 1970
    end

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

1973
      expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
1974 1975
    end

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

1979
      expect(project.ci_config_path).to eq('foo//.gitlab_ci.yml')
1980 1981 1982
    end
  end

1983
  describe '#latest_successful_build_for' do
1984
    let(:project) { create(:project, :repository) }
1985
    let(:pipeline) { create_pipeline(project) }
L
Lin Jen-Shin 已提交
1986 1987

    context 'with many builds' do
1988
      it 'gives the latest builds from latest pipeline' do
1989 1990
        pipeline1 = create_pipeline(project)
        pipeline2 = create_pipeline(project)
1991 1992
        create_build(pipeline1, 'test')
        create_build(pipeline1, 'test2')
1993
        build1_p2 = create_build(pipeline2, 'test')
1994
        create_build(pipeline2, 'test2')
L
Lin Jen-Shin 已提交
1995

1996 1997
        expect(project.latest_successful_build_for(build1_p2.name))
          .to eq(build1_p2)
L
Lin Jen-Shin 已提交
1998 1999
      end
    end
L
Lin Jen-Shin 已提交
2000

L
Lin Jen-Shin 已提交
2001
    context 'with succeeded pipeline' do
L
Lin Jen-Shin 已提交
2002
      let!(:build) { create_build }
2003

L
Lin Jen-Shin 已提交
2004
      context 'standalone pipeline' do
2005
        it 'returns builds for ref for default_branch' do
2006 2007 2008
          expect(project.latest_successful_build_for(build.name))
            .to eq(build)
        end
2009

2010 2011 2012
        it 'returns empty relation if the build cannot be found' do
          expect(project.latest_successful_build_for('TAIL'))
            .to be_nil
2013
        end
2014
      end
2015

2016 2017 2018 2019
      context 'with some pending pipeline' do
        before do
          create_build(create_pipeline(project, 'pending'))
        end
2020

2021 2022 2023
        it 'gives the latest build from latest pipeline' do
          expect(project.latest_successful_build_for(build.name))
            .to eq(build)
2024
        end
2025 2026
      end
    end
2027

2028 2029 2030 2031
    context 'with pending pipeline' do
      it 'returns empty relation' do
        pipeline.update(status: 'pending')
        pending_build = create_build(pipeline)
2032

2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067
        expect(project.latest_successful_build_for(pending_build.name)).to be_nil
      end
    end
  end

  describe '#latest_successful_build_for!' do
    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')

        expect(project.latest_successful_build_for(build1_p2.name))
          .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
          expect(project.latest_successful_build_for!(build.name))
            .to eq(build)
        end

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

L
Lin Jen-Shin 已提交
2071
      context 'with some pending pipeline' do
2072
        before do
2073
          create_build(create_pipeline(project, 'pending'))
2074 2075
        end

L
Lin Jen-Shin 已提交
2076
        it 'gives the latest build from latest pipeline' do
2077 2078
          expect(project.latest_successful_build_for!(build.name))
            .to eq(build)
2079
        end
2080 2081 2082 2083
      end
    end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      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
2155 2156 2157 2158
    end

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

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

R
Rémy Coutable 已提交
2167
  describe '#gitlab_project_import?' do
2168
    subject(:project) { build(:project, import_type: 'gitlab_project') }
R
Rémy Coutable 已提交
2169 2170 2171 2172 2173

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

  describe '#gitea_import?' do
2174
    subject(:project) { build(:project, import_type: 'gitea') }
R
Rémy Coutable 已提交
2175 2176 2177 2178

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

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

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

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

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

      is_expected.to be_falsy
    end
  end

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

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

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

      update_remote_mirrors
    end

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

      expect_any_instance_of(RemoteMirror).not_to receive(:sync)

      update_remote_mirrors
    end

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

      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

2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268
  describe '#ancestors_upto', :nested_groups do
    let(:parent) { create(:group) }
    let(:child) { create(:group, parent: parent) }
    let(:child2) { create(:group, parent: child) }
    let(:project) { create(:project, namespace: child2) }

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

    it 'includes ancestors upto but excluding the given ancestor' do
      expect(project.ancestors_upto(parent)).to contain_exactly(child2, child)
    end
2269 2270 2271 2272 2273 2274 2275 2276 2277 2278

    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
2279 2280
  end

2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301
  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

    context 'in a nested group', :nested_groups do
      let(:root) { create(:group) }
      let(:child) { create(:group, parent: root) }
      let(:project) { create(:project, group: child) }

      it { is_expected.to eq(root) }
    end
2302 2303
  end

2304
  describe '#lfs_enabled?' do
2305
    let(:project) { create(:project) }
2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365

    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 已提交
2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377
  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

2378
  describe '#change_head' do
2379
    let(:project) { create(:project, :repository) }
2380

2381 2382 2383 2384 2385
    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

2386
    it 'calls the before_change_head and after_change_head methods' do
2387
      expect(project.repository).to receive(:before_change_head)
2388 2389
      expect(project.repository).to receive(:after_change_head)

2390 2391 2392
      project.change_head(project.default_branch)
    end

2393 2394 2395 2396 2397 2398
    it 'updates commit count' do
      expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:commit_count])

      project.change_head(project.default_branch)
    end

2399 2400 2401 2402 2403 2404 2405 2406 2407 2408
    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 已提交
2409

2410
  context 'forks' do
B
Bob Van Landuyt 已提交
2411 2412
    include ProjectForksHelper

2413 2414 2415 2416 2417
    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 已提交
2418
        expect(project.fork_network.projects).to include(forked_project)
2419 2420 2421 2422 2423
      end

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

B
Bob Van Landuyt 已提交
2424
        expect(project.fork_network.projects).to include(other_fork)
2425 2426 2427 2428 2429
      end

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

B
Bob Van Landuyt 已提交
2430
        expect(forked_project.fork_network.projects).to include(other_fork)
2431 2432 2433
      end

      it 'includes the base project' do
B
Bob Van Landuyt 已提交
2434
        expect(forked_project.fork_network.projects).to include(project.reload)
2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460
      end
    end

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

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

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

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

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

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

        expect(forked_project.in_fork_network_of?(other_project)).to be_falsy
      end
    end
2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473

    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
2474 2475 2476 2477

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

2480 2481 2482 2483 2484 2485
    describe '#forks' do
      it 'includes direct forks of the project' do
        expect(project.forks).to contain_exactly(forked_project)
      end
    end

2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502
    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
2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518

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

      before do
        project.lfs_objects << lfs_object
      end

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

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

2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534
  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 已提交
2535 2536 2537 2538 2539 2540 2541 2542 2543 2544
  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

Y
Yorick Peterse 已提交
2545
  describe '#pushes_since_gc' do
2546
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567

    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
2568
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581

    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
2582
    let(:project) { create(:project) }
Y
Yorick Peterse 已提交
2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595

    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
2596

2597 2598
  describe '#deployment_variables' do
    context 'when project has no deployment service' do
2599
      let(:project) { create(:project) }
2600 2601 2602 2603 2604 2605

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

2606 2607 2608 2609 2610 2611 2612 2613
    context 'when project uses mock deployment service' do
      let(:project) { create(:mock_deployment_project) }

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

2614
    context 'when project has a deployment service' do
2615
      shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
2616 2617
        it 'returns variables from this service' do
          expect(project.deployment_variables).to include(
2618
            { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false, masked: true }
2619 2620 2621 2622 2623 2624 2625
          )
        end
      end

      context 'when user configured kubernetes from Integration > Kubernetes' do
        let(:project) { create(:kubernetes_project) }

2626
        it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
2627 2628
      end

2629
      context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has not been executed' do
2630 2631
        let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
        let(:project) { cluster.project }
2632

2633
        it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
2634
      end
2635 2636

      context 'when user configured kubernetes from CI/CD > Clusters and KubernetesNamespace migration has been executed' do
T
Thong Kuah 已提交
2637
        let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, :with_token) }
2638 2639 2640 2641 2642
        let!(:cluster) { kubernetes_namespace.cluster }
        let(:project) { kubernetes_namespace.project }

        it 'should return token from kubernetes namespace' do
          expect(project.deployment_variables).to include(
2643
            { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
2644 2645 2646
          )
        end
      end
2647 2648 2649
    end
  end

2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671
  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

2672
  describe '#ci_variables_for' do
2673
    let(:project) { create(:project) }
2674

2675
    let!(:ci_variable) do
2676 2677 2678 2679 2680 2681 2682
      create(:ci_variable, value: 'secret', project: project)
    end

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

2683
    subject { project.reload.ci_variables_for(ref: 'ref') }
L
Lin Jen-Shin 已提交
2684 2685 2686 2687 2688

    before do
      stub_application_setting(
        default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end
2689 2690 2691

    shared_examples 'ref is protected' do
      it 'contains all the variables' do
2692
        is_expected.to contain_exactly(ci_variable, protected_variable)
2693 2694 2695 2696
      end
    end

    context 'when the ref is not protected' do
2697 2698 2699 2700
      before do
        allow(project).to receive(:protected_for?).with('ref').and_return(false)
      end

2701 2702
      it 'contains only the CI variables' do
        is_expected.to contain_exactly(ci_variable)
2703 2704 2705
      end
    end

2706 2707
    context 'when the ref is a protected branch' do
      before do
2708
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
2709
      end
2710 2711 2712 2713 2714 2715

      it_behaves_like 'ref is protected'
    end

    context 'when the ref is a protected tag' do
      before do
2716
        allow(project).to receive(:protected_for?).with('ref').and_return(true)
2717 2718 2719
      end

      it_behaves_like 'ref is protected'
2720 2721 2722
    end
  end

2723
  describe '#any_lfs_file_locks?', :request_store do
2724
    let!(:project) { create(:project) }
2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738

    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

2739
  describe '#protected_for?' do
2740
    let(:project) { create(:project, :repository) }
2741

2742
    subject { project.protected_for?(ref) }
2743

2744
    shared_examples 'ref is not protected' do
2745 2746 2747 2748 2749 2750
      before do
        stub_application_setting(
          default_branch_protection: Gitlab::Access::PROTECTION_NONE)
      end

      it 'returns false' do
2751
        is_expected.to be false
2752 2753 2754
      end
    end

2755
    shared_examples 'ref is protected branch' do
2756
      before do
2757
        create(:protected_branch, name: 'master', project: project)
2758 2759 2760
      end

      it 'returns true' do
2761
        is_expected.to be true
2762 2763 2764
      end
    end

2765
    shared_examples 'ref is protected tag' do
2766
      before do
2767
        create(:protected_tag, name: 'v1.0.0', project: project)
2768 2769 2770
      end

      it 'returns true' do
2771
        is_expected.to be true
2772 2773
      end
    end
2774

2775 2776
    context 'when ref is nil' do
      let(:ref) { nil }
2777

2778
      it 'returns false' do
2779
        is_expected.to be false
2780
      end
2781 2782 2783 2784 2785
    end

    context 'when ref is ref name' do
      context 'when ref is ambiguous' do
        let(:ref) { 'ref' }
2786 2787

        before do
2788 2789
          project.repository.add_branch(project.creator, 'ref', 'master')
          project.repository.add_tag(project.creator, 'ref', 'master')
2790 2791
        end

2792 2793
        it 'raises an error' do
          expect { subject }.to raise_error(Repository::AmbiguousRefError)
2794 2795
        end
      end
2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813

      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
2814 2815 2816 2817 2818 2819 2820 2821

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

        it 'returns false' do
          is_expected.to be false
        end
      end
2822 2823
    end

2824 2825 2826
    context 'when ref is full ref' do
      context 'when the ref is not protected' do
        let(:ref) { 'refs/heads/master' }
2827

2828
        it_behaves_like 'ref is not protected'
2829 2830
      end

2831 2832 2833 2834 2835 2836 2837 2838 2839 2840
      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'
2841 2842
      end

2843 2844 2845
      context 'when branch ref name is a full tag ref' do
        let(:ref) { 'refs/tags/something' }

2846
        before do
2847
          project.repository.add_branch(project.creator, ref, 'master')
2848 2849
        end

2850 2851
        context 'when ref is not protected' do
          it 'returns false' do
2852
            is_expected.to be false
2853 2854 2855 2856 2857 2858 2859 2860 2861
          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
2862
            is_expected.to be true
2863
          end
2864 2865
        end
      end
2866 2867 2868 2869 2870 2871 2872

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

        it 'returns false' do
          is_expected.to be false
        end
2873 2874 2875 2876
      end
    end
  end

M
Markus Koller 已提交
2877
  describe '#update_project_statistics' do
2878
    let(:project) { create(:project) }
M
Markus Koller 已提交
2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896

    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

2897
  describe 'inside_path' do
2898 2899 2900
    let!(:project1) { create(:project, namespace: create(:namespace, path: 'name_pace')) }
    let!(:project2) { create(:project) }
    let!(:project3) { create(:project, namespace: create(:namespace, path: 'namespace')) }
2901
    let!(:path) { project1.namespace.full_path }
2902

2903
    it 'returns correct project' do
2904
      expect(described_class.inside_path(path)).to eq([project1])
2905
    end
2906 2907
  end

D
Douwe Maan 已提交
2908
  describe '#route_map_for' do
2909
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
2910 2911 2912 2913 2914 2915 2916 2917
    let(:route_map) do
      <<-MAP.strip_heredoc
      - source: /source/(.*)/
        public: '\\1'
      MAP
    end

    before do
2918
      project.repository.create_file(User.last, '.gitlab/route-map.yml', route_map, message: 'Add .gitlab/route-map.yml', branch_name: 'master')
D
Douwe Maan 已提交
2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945
    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
2946
    let(:project) { create(:project, :repository) }
D
Douwe Maan 已提交
2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983
    let(:route_map) do
      Gitlab::RouteMap.new(<<-MAP.strip_heredoc)
        - source: /source/(.*)/
          public: '\\1'
      MAP
    end
    let(:sha) { project.commit.id }

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

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

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

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

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

2984
  describe '#parent' do
2985
    let(:project) { create(:project) }
2986 2987 2988 2989

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

2990 2991 2992 2993 2994 2995
  describe '#parent_id' do
    let(:project) { create(:project) }

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

2996
  describe '#parent_changed?' do
2997
    let(:project) { create(:project) }
2998

2999 3000 3001
    before do
      project.namespace_id = 7
    end
3002 3003 3004 3005

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

3006 3007 3008
  def enable_lfs
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
  end
K
Kamil Trzcinski 已提交
3009

3010
  describe '#pages_url' do
3011 3012
    let(:group) { create(:group, name: 'Group') }
    let(:nested_group) { create(:group, parent: group) }
K
Kamil Trzcinski 已提交
3013 3014 3015 3016 3017 3018 3019 3020 3021
    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

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

3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035
      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 已提交
3036 3037
    end

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

3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052
      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 已提交
3053 3054
    end
  end
3055 3056

  describe '#http_url_to_repo' do
3057
    let(:project) { create(:project) }
3058

3059 3060 3061
    it 'returns the url to the repo without a username' do
      expect(project.http_url_to_repo).to eq("#{project.web_url}.git")
      expect(project.http_url_to_repo).not_to include('@')
3062 3063
    end
  end
3064

3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075
  describe '#lfs_http_url_to_repo' do
    let(:project) { create(:project) }

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

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

3076
  describe '#pipeline_status' do
3077
    let(:project) { create(:project, :repository) }
3078
    it 'builds a pipeline status' do
3079
      expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
3080 3081 3082 3083 3084 3085
    end

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

  describe '#append_or_update_attribute' do
3088
    let(:project) { create(:project) }
3089 3090 3091

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

3094 3095
      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
        .to raise_error(ActiveRecord::RecordNotSaved, error_message)
3096 3097
    end

G
George Tsiolis 已提交
3098
    it 'updates the project successfully' do
3099 3100
      merge_request = create(:merge_request, target_project: project, source_project: project)

3101 3102
      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
        .not_to raise_error
3103 3104
    end
  end
3105

3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123
  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

3124 3125
  describe '#last_repository_updated_at' do
    it 'sets to created_at upon creation' do
3126
      project = create(:project, created_at: 2.hours.ago)
3127 3128 3129 3130

      expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
    end
  end
3131 3132 3133 3134 3135

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

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

3139
    let!(:public_project) { create(:project, :public) }
3140 3141 3142

    context 'with a user' do
      let(:projects) do
3143
        described_class.all.public_or_visible_to_user(user)
3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156
      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
3157
        projects = described_class.all.public_or_visible_to_user
3158 3159 3160 3161

        expect(projects).to eq([public_project])
      end
    end
3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208

    context 'with requested visibility levels' do
      set(:internal_project) { create(:project, :internal, :repository) }
      set(:private_project_2) { create(:project, :private) }

      context 'with admin user' do
        set(:admin) { create(:admin) }

        it 'returns all projects' do
          projects = described_class.all.public_or_visible_to_user(admin, [])

          expect(projects).to match_array([public_project, private_project, private_project_2, internal_project])
        end

        it 'returns all public and private projects' do
          projects = described_class.all.public_or_visible_to_user(admin, [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::PRIVATE])

          expect(projects).to match_array([public_project, private_project, private_project_2])
        end

        it 'returns all private projects' do
          projects = described_class.all.public_or_visible_to_user(admin, [Gitlab::VisibilityLevel::PRIVATE])

          expect(projects).to match_array([private_project, private_project_2])
        end
      end

      context 'with regular user' do
        it 'returns authorized projects' do
          projects = described_class.all.public_or_visible_to_user(user, [])

          expect(projects).to match_array([public_project, private_project, internal_project])
        end

        it "returns user's public and private projects" do
          projects = described_class.all.public_or_visible_to_user(user, [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::PRIVATE])

          expect(projects).to match_array([public_project, private_project])
        end

        it 'returns one private project' do
          projects = described_class.all.public_or_visible_to_user(user, [Gitlab::VisibilityLevel::PRIVATE])

          expect(projects).to eq([private_project])
        end
      end
    end
3209
  end
3210

3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270
  describe '.with_feature_available_for_user' do
    let!(:user) { create(:user) }
    let!(:feature) { MergeRequest }
    let!(:project) { create(:project, :public, :merge_requests_enabled) }

    subject { described_class.with_feature_available_for_user(feature, user) }

    context 'when user has access to project' do
      subject { described_class.with_feature_available_for_user(feature, user) }

      before do
        project.add_guest(user)
      end

      context 'when public project' do
        context 'when feature is public' do
          it 'returns project' do
            is_expected.to include(project)
          end
        end

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

          it 'returns project when user has access to the feature' do
            project.add_maintainer(user)

            is_expected.to include(project)
          end

          it 'does not return project when user does not have the minimum access level required' do
            is_expected.not_to include(project)
          end
        end
      end

      context 'when private project' do
        let!(:project) { create(:project) }

        it 'returns project when user has access to the feature' do
          project.add_maintainer(user)

          is_expected.to include(project)
        end

        it 'does not return project when user does not have the minimum access level required' do
          is_expected.not_to include(project)
        end
      end
    end

    context 'when user does not have access to project' do
      let!(:project) { create(:project) }

      it 'does not return project when user cant access project' do
        is_expected.not_to include(project)
      end
    end
  end

3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288
  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) }

3289
      it { is_expected.to be(true) }
3290 3291 3292
    end
  end

3293
  describe '#remove_private_deploy_keys' do
3294
    let!(:project) { create(:project) }
3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309

    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
3310
          another_project = create(:project)
3311 3312
          create(:deploy_keys_project, deploy_key: key, project: another_project)
        end
3313

3314 3315
        it 'does not remove the key' do
          project.remove_private_deploy_keys
3316

3317 3318 3319 3320 3321 3322 3323 3324
          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) }
3325

3326 3327
      it 'does not remove the key' do
        project.remove_private_deploy_keys
3328

3329 3330
        expect(project.deploy_keys).to include(key)
      end
3331 3332
    end
  end
3333

3334 3335
  describe '#remove_pages' do
    let(:project) { create(:project) }
3336
    let(:namespace) { project.namespace }
3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347
    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

3348 3349 3350 3351 3352 3353 3354 3355
    it 'removes the pages directory' do
      expect_any_instance_of(Projects::UpdatePagesConfigurationService).to receive(:execute)
      expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return(true)
      expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, namespace.full_path, anything)

      project.remove_pages
    end

3356
    it 'is a no-op when there is no namespace' do
3357 3358
      project.namespace.delete
      project.reload
3359 3360 3361 3362 3363 3364

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

      project.remove_pages
    end
3365 3366 3367 3368 3369 3370

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

      project.destroy
    end
3371 3372
  end

3373
  describe '#remove_export' do
3374 3375
    let(:project) { create(:project, :with_export) }

3376
    it 'removes the export' do
3377 3378
      project.remove_exports

J
James Lopez 已提交
3379
      expect(project.export_file_exists?).to be_falsey
3380 3381 3382
    end
  end

3383 3384 3385 3386
  describe '#forks_count' do
    it 'returns the number of forks' do
      project = build(:project)

F
Francisco Lopez 已提交
3387
      expect_any_instance_of(Projects::ForksCountService).to receive(:count).and_return(1)
3388 3389 3390 3391

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

3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419
  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

3420
  context 'legacy storage' do
G
Gabriel Mazetto 已提交
3421
    set(:project) { create(:project, :repository, :legacy_storage) }
3422
    let(:gitlab_shell) { Gitlab::Shell.new }
3423
    let(:project_storage) { project.send(:storage) }
3424

3425 3426 3427 3428
    before do
      allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
    end

3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440
    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

3441
    describe '#ensure_storage_path_exists' do
3442
      it 'delegates to gitlab_shell to ensure namespace is created' do
3443
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, project.base_dir)
3444

3445
        project.ensure_storage_path_exists
3446 3447 3448
      end
    end

3449 3450
    describe '#legacy_storage?' do
      it 'returns true when storage_version is nil' do
3451
        project = build(:project, storage_version: nil)
3452 3453 3454

        expect(project.legacy_storage?).to be_truthy
      end
3455 3456 3457 3458 3459 3460 3461 3462 3463 3464

      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
3465
        expect(project.hashed_storage?(:repository)).to be_falsey
3466
      end
3467 3468
    end

3469 3470 3471 3472 3473
    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
3474 3475

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

3478 3479 3480 3481
      it 'returns true' do
        expect(project.migrate_to_hashed_storage!).to be_truthy
      end

G
Gabriel Mazetto 已提交
3482 3483
      it 'does not run validation' do
        expect(project).not_to receive(:valid?)
3484 3485 3486 3487

        project.migrate_to_hashed_storage!
      end

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

3491
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
3492 3493 3494 3495

        project.migrate_to_hashed_storage!
      end

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

3499
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
3500 3501 3502 3503

        project.migrate_to_hashed_storage!
      end

3504 3505
      it 'schedules HashedStorage::ProjectMigrateWorker' do
        expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_async).with(project.id)
3506 3507 3508 3509

        project.migrate_to_hashed_storage!
      end
    end
3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523

    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
3524 3525 3526
  end

  context 'hashed storage' do
G
Gabriel Mazetto 已提交
3527
    set(:project) { create(:project, :repository, skip_disk_validation: true) }
3528
    let(:gitlab_shell) { Gitlab::Shell.new }
3529
    let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
N
Nick Thomas 已提交
3530 3531
    let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
    let(:hashed_path) { File.join(hashed_prefix, hash) }
3532 3533

    before do
3534
      stub_application_setting(hashed_storage_enabled: true)
3535 3536
    end

3537 3538 3539 3540 3541 3542 3543
    describe '#legacy_storage?' do
      it 'returns false' do
        expect(project.legacy_storage?).to be_falsey
      end
    end

    describe '#hashed_storage?' do
3544 3545
      it 'returns true if rolled out' do
        expect(project.hashed_storage?(:attachments)).to be_truthy
3546 3547
      end

3548 3549
      it 'returns false when not rolled out yet' do
        project.storage_version = 1
3550

3551
        expect(project.hashed_storage?(:attachments)).to be_falsey
3552 3553 3554
      end
    end

3555 3556
    describe '#base_dir' do
      it 'returns base_dir based on hash of project id' do
N
Nick Thomas 已提交
3557
        expect(project.base_dir).to eq(hashed_prefix)
3558 3559 3560 3561
      end
    end

    describe '#disk_path' do
3562
      it 'returns disk_path based on hash of project id' do
3563 3564 3565 3566
        expect(project.disk_path).to eq(hashed_path)
      end
    end

3567
    describe '#ensure_storage_path_exists' do
3568
      it 'delegates to gitlab_shell to ensure namespace is created' do
3569 3570
        allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)

3571
        expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, hashed_prefix)
3572

3573
        project.ensure_storage_path_exists
3574 3575 3576
      end
    end

3577 3578 3579 3580 3581
    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
3582 3583

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

3586 3587 3588 3589
      it 'returns nil' do
        expect(project.migrate_to_hashed_storage!).to be_nil
      end

T
Toon Claes 已提交
3590
      it 'does not flag as read-only' do
3591 3592
        expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only }
      end
3593 3594

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

G
Gabriel Mazetto 已提交
3598
          Sidekiq::Testing.fake! do
3599
            expect { project.migrate_to_hashed_storage! }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1)
G
Gabriel Mazetto 已提交
3600
          end
3601 3602
        end
      end
3603
    end
3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623

    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
3624
          expect { project.rollback_to_legacy_storage! }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1)
3625 3626 3627
        end
      end
    end
3628 3629
  end

3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652
  describe '#has_ci?' do
    set(:project) { create(:project) }
    let(:repository) { double }

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

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

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

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

3653 3654
      it "CI is available" do
        expect(project).to have_ci
3655 3656
      end

3657
      context 'when auto devops is disabled' do
3658
        before do
3659
          stub_application_setting(auto_devops_enabled: false)
3660 3661
        end

3662 3663
        it "CI is not available" do
          expect(project).not_to have_ci
3664 3665 3666 3667 3668 3669
        end
      end
    end
  end

  describe '#auto_devops_enabled?' do
3670 3671 3672 3673 3674
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

3675 3676 3677 3678
    set(:project) { create(:project) }

    subject { project.auto_devops_enabled? }

3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694
    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

3695 3696 3697 3698 3699
    context 'when enabled in settings' do
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

3700
      it { is_expected.to be_truthy }
3701 3702 3703 3704 3705 3706 3707 3708
    end

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

      it { is_expected.to be_falsey }
3709 3710 3711 3712 3713 3714

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

3715
        it { is_expected.to be_truthy }
3716 3717 3718 3719
      end

      context 'when explicitly disabled' do
        before do
3720
          create(:project_auto_devops, :disabled, project: project)
3721 3722
        end

3723
        it { is_expected.to be_falsey }
3724 3725 3726
      end
    end

3727 3728 3729 3730 3731 3732 3733
    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 }

3734
      before do
3735 3736
        stub_application_setting(auto_devops_enabled: instance_enabled)
        project.update!(namespace: parent_group)
3737 3738
      end

3739 3740
      context 'when enabled on parent' do
        let(:parent_group) { create(:group, :auto_devops_enabled) }
3741

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

3746 3747 3748 3749 3750
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_truthy }
        end
3751 3752
      end

3753 3754 3755 3756 3757
      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 }
3758
        end
3759

3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804
        context 'when auto devops instance disabled' do
          let(:instance_disabled) { false }

          it { is_expected.to be_falsy }
        end
      end

      context 'when enabled on root parent', :nested_groups do
        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

      context 'when disabled on root parent', :nested_groups do
        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
3805 3806 3807 3808
      end
    end
  end

3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 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
  describe '#has_auto_devops_implicitly_enabled?' do
    set(:project) { create(:project) }

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

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

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

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

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

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

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

        it 'does not have auto devops implicitly disabled' do
          expect(project).not_to have_auto_devops_implicitly_enabled
        end
      end
    end
3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867

    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
3868 3869
  end

3870
  describe '#has_auto_devops_implicitly_disabled?' do
3871 3872
    set(:project) { create(:project) }

3873 3874 3875 3876 3877
    before do
      allow(Feature).to receive(:enabled?).and_call_original
      Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
    end

3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896
    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
3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916

    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

3917 3918
      context 'when force_autodevops_on_by_default is enabled for the project' do
        before do
3919 3920
          create(:project_auto_devops, project: project, enabled: false)

3921 3922 3923 3924 3925 3926 3927 3928
          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

3929 3930 3931
      context 'when disabled on group' do
        it 'has auto devops implicitly disabled' do
          project.update!(namespace: create(:group, :auto_devops_disabled))
3932

3933
          expect(project).to have_auto_devops_implicitly_disabled
3934 3935 3936
        end
      end

3937 3938 3939 3940
      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)
3941

3942
          expect(project).to have_auto_devops_implicitly_disabled
3943 3944 3945 3946 3947
        end
      end
    end
  end

3948 3949 3950 3951 3952 3953 3954 3955
  describe '#api_variables' do
    set(:project) { create(:project) }

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

3956
    it 'contains a URL variable for every supported API version' do
3957 3958 3959
      # 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|
3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972
        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

  describe '#auto_devops_variables' do
3973 3974 3975 3976
    set(:project) { create(:project) }

    subject { project.auto_devops_variables }

3977
    context 'when enabled in instance settings' do
3978 3979 3980 3981
      before do
        stub_application_setting(auto_devops_enabled: true)
      end

3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002
      context 'when domain is empty' do
        before do
          stub_application_setting(auto_devops_domain: nil)
        end

        it 'variables does not include AUTO_DEVOPS_DOMAIN' do
          is_expected.not_to include(domain_variable)
        end
      end

      context 'when domain is configured' do
        before do
          stub_application_setting(auto_devops_domain: 'example.com')
        end

        it 'variables includes AUTO_DEVOPS_DOMAIN' do
          is_expected.to include(domain_variable)
        end
      end
    end

G
George Tsiolis 已提交
4003
    context 'when explicitly enabled' do
4004 4005 4006 4007 4008
      context 'when domain is empty' do
        before do
          create(:project_auto_devops, project: project, domain: nil)
        end

4009 4010
        it 'variables does not include AUTO_DEVOPS_DOMAIN' do
          is_expected.not_to include(domain_variable)
4011 4012 4013 4014 4015 4016 4017 4018
        end
      end

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

4019 4020
        it 'variables includes AUTO_DEVOPS_DOMAIN' do
          is_expected.to include(domain_variable)
4021 4022 4023
        end
      end
    end
4024 4025 4026 4027

    def domain_variable
      { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true }
    end
4028
  end
4029 4030 4031 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

  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
4057
        expect(project.ci_pipelines).to receive(:latest_successful_for).with('foo')
4058 4059 4060 4061 4062 4063

        project.latest_successful_pipeline_for('foo')
      end
    end
  end

S
Stan Hu 已提交
4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074
  describe '#check_repository_path_availability' do
    let(:project) { build(:project) }

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

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

4075 4076 4077 4078 4079 4080 4081 4082 4083 4084
  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)

4085
      expect(project.ci_pipelines).to receive(:latest_successful_for)
4086 4087 4088 4089 4090 4091 4092 4093 4094 4095
        .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
4096 4097

  describe '#after_import' do
4098
    let(:project) { create(:project) }
4099
    let(:import_state) { create(:import_state, project: project) }
4100 4101 4102

    it 'runs the correct hooks' do
      expect(project.repository).to receive(:after_import)
4103
      expect(project.wiki.repository).to receive(:after_import)
4104
      expect(import_state).to receive(:finish)
4105
      expect(project).to receive(:update_project_counter_caches)
4106
      expect(import_state).to receive(:remove_jid)
4107
      expect(project).to receive(:after_create_default_branch)
4108
      expect(project).to receive(:refresh_markdown_cache!)
4109
      expect(InternalId).to receive(:flush_records!).with(project: project)
4110 4111 4112

      project.after_import
    end
4113 4114 4115 4116

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

4117 4118 4119 4120
      before do
        create(:import_state, :started, project: project)
      end

4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153
      it 'does not protect when branch protection is disabled' do
        stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)

        project.after_import

        expect(project.protected_branches).to be_empty
      end

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

        project.after_import

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

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

        project.after_import

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

      it 'protects default branch' do
        project.after_import

        expect(project.protected_branches).not_to be_empty
        expect(project.default_branch).to eq(project.protected_branches.first.name)
4154 4155
        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])
4156 4157
      end
    end
4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188
  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
4189

4190 4191 4192 4193 4194 4195
  describe '#write_repository_config' do
    set(:project) { create(:project, :repository) }

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

4196
      expect(rugged_config['gitlab.fullpath']).to eq project.full_path
4197 4198 4199 4200 4201
    end

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

4202
      expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
4203 4204 4205 4206 4207 4208 4209 4210
    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
4211 4212

  describe '#execute_hooks' do
4213
    let(:data) { { ref: 'refs/heads/master', data: 'data' } }
D
Duana Saskia 已提交
4214
    it 'executes active projects hooks with the specified scope' do
4215 4216 4217 4218
      hook = create(:project_hook, merge_requests_events: false, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([hook])
4219
      project = create(:project, hooks: [hook])
4220 4221 4222

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

4223
      project.execute_hooks(data, :push_hooks)
4224 4225 4226
    end

    it 'does not execute project hooks that dont match the specified scope' do
4227
      hook = create(:project_hook, merge_requests_events: true, push_events: false)
4228 4229 4230 4231
      project = create(:project, hooks: [hook])

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

4232
      project.execute_hooks(data, :push_hooks)
4233 4234
    end

D
Duana Saskia 已提交
4235
    it 'does not execute project hooks which are not active' do
4236 4237 4238 4239
      hook = create(:project_hook, push_events: true)
      expect(ProjectHook).to receive(:select_active)
        .with(:push_hooks, data)
        .and_return([])
D
Duana Saskia 已提交
4240 4241 4242 4243
      project = create(:project, hooks: [hook])

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

4244
      project.execute_hooks(data, :push_hooks)
4245 4246 4247
    end

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

4250
      project = build(:project)
4251
      project.execute_hooks(data, :merge_request_hooks)
4252
    end
4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265

    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
4266
          project.execute_hooks(data, :merge_request_hooks)
4267 4268 4269
        end
      end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
    end
4270
  end
4271 4272 4273

  describe '#badges' do
    let(:project_group) { create(:group) }
4274
    let(:project) { create(:project, path: 'avatar', namespace: project_group) }
4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287

    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

4288
    if Group.supports_nested_objects?
4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302
      context 'with nested_groups' do
        let(:parent_group) { create(:group) }

        before do
          create_list(:group_badge, 2, group: project_group)
          project_group.update(parent: parent_group)
        end

        it 'returns the project and the project nested groups badges' do
          expect(project.badges.count).to eq 5
        end
      end
    end
  end
4303

4304 4305
  context 'with cross project merge requests' do
    let(:user) { create(:user) }
4306 4307
    let(:target_project) { create(:project, :repository) }
    let(:project) { fork_project(target_project, nil, repository: true) }
4308 4309 4310 4311 4312 4313 4314 4315 4316 4317
    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
4318 4319 4320 4321
    let!(:merge_request) do
      create(
        :merge_request,
        target_project: target_project,
4322
        target_branch: 'target-branch',
4323 4324
        source_project: project,
        source_branch: 'awesome-feature-1',
4325
        allow_collaboration: true
4326 4327 4328 4329
      )
    end

    before do
4330
      target_project.add_developer(user)
4331 4332
    end

4333 4334 4335 4336
    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
4337

4338 4339
      it 'does not include closed merge requests' do
        merge_request.close
4340

4341 4342 4343 4344 4345 4346
        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)
4347

4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359
        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
4360 4361
    end

4362 4363 4364
    describe '#any_branch_allows_collaboration?' do
      it 'allows access when there are merge requests open allowing collaboration' do
        expect(project.any_branch_allows_collaboration?(user))
4365 4366 4367
          .to be_truthy
      end

4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378
      it 'does not allow access when there are no merge requests open allowing collaboration' do
        merge_request.close!

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

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

4382 4383 4384 4385
      it 'does not allow guest users access' do
        guest = create(:user)
        target_project.add_guest(guest)

4386
        expect(project.branch_allows_collaboration?(guest, 'awesome-feature-1'))
4387 4388 4389
          .to be_falsy
      end

4390
      it 'does not allow access to branches for which the merge request was closed' do
4391 4392
        create(:merge_request, :closed,
               target_project: target_project,
4393
               target_branch: 'target-branch',
4394 4395
               source_project: project,
               source_branch: 'rejected-feature-1',
4396
               allow_collaboration: true)
4397

4398
        expect(project.branch_allows_collaboration?(user, 'rejected-feature-1'))
4399 4400 4401
          .to be_falsy
      end

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

4405
        expect(project.branch_allows_collaboration?(user, 'awesome-feature-1'))
4406 4407 4408
          .to be_falsy
      end

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

4413
          expect { 2.times { described_class.find(project.id).branch_allows_collaboration?(user, 'awesome-feature-1') } }
4414
            .not_to exceed_query_limit(control).with_threshold(2)
4415
        end
4416 4417 4418
      end
    end
  end
R
Rob Watson 已提交
4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 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

  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
4464

4465
  describe '#toggle_ci_cd_settings!' do
4466
    it 'toggles the value on #settings' do
4467
      project = create(:project, group_runners_enabled: false)
4468 4469 4470

      expect(project.group_runners_enabled).to be false

4471
      project.toggle_ci_cd_settings!(:group_runners_enabled)
4472 4473 4474 4475

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

4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496
  describe '#gitlab_deploy_token' do
    let(:project) { create(:project) }

    subject { project.gitlab_deploy_token }

    context 'when there is a gitlab deploy token associated' do
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }

      it { is_expected.to eq(deploy_token) }
    end

    context 'when there is no a gitlab deploy token associated' do
      it { is_expected.to be_nil }
    end

    context 'when there is a gitlab deploy token associated but is has been revoked' do
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :revoked, projects: [project]) }
      it { is_expected.to be_nil }
    end

4497
    context 'when there is a gitlab deploy token associated but it is expired' do
4498 4499 4500 4501
      let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) }

      it { is_expected.to be_nil }
    end
4502

4503
    context 'when there is a deploy token associated with a different name' do
4504 4505 4506 4507
      let!(:deploy_token) { create(:deploy_token, projects: [project]) }

      it { is_expected.to be_nil }
    end
4508 4509 4510 4511 4512 4513 4514

    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
4515
  end
J
Jan Provaznik 已提交
4516 4517

  context 'with uploads' do
4518
    it_behaves_like 'model with uploads', true do
J
Jan Provaznik 已提交
4519 4520 4521 4522 4523
      let(:model_object) { create(:project, :with_avatar) }
      let(:upload_attribute) { :avatar }
      let(:uploader_class) { AttachmentUploader }
    end
  end
4524

4525 4526 4527 4528 4529 4530 4531 4532 4533 4534
  context '#commits_by' do
    let(:project) { create(:project, :repository) }
    let(:commits) { project.repository.commits('HEAD', limit: 3).commits }
    let(:commit_shas) { commits.map(&:id) }

    it 'retrieves several commits from the repository by oid' do
      expect(project.commits_by(oids: commit_shas)).to eq commits
    end
  end

4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575
  context '#members_among' do
    let(:users) { create_list(:user, 3) }
    set(:group) { create(:group) }
    set(:project) { create(:project, namespace: group) }

    before do
      project.add_guest(users.first)
      project.group.add_maintainer(users.last)
    end

    context 'when users is an Array' do
      it 'returns project members among the users' do
        expect(project.members_among(users)).to eq([users.first, users.last])
      end

      it 'maintains input order' do
        expect(project.members_among(users.reverse)).to eq([users.last, users.first])
      end

      it 'returns empty array if users is empty' do
        result = project.members_among([])

        expect(result).to be_empty
      end
    end

    context 'when users is a relation' do
      it 'returns project members among the users' do
        result = project.members_among(User.where(id: users.map(&:id)))

        expect(result).to be_a(ActiveRecord::Relation)
        expect(result).to eq([users.first, users.last])
      end

      it 'returns empty relation if users is empty' do
        result = project.members_among(User.none)

        expect(result).to be_a(ActiveRecord::Relation)
        expect(result).to be_empty
      end
    end
4576
  end
4577

4578 4579
  describe "#find_or_initialize_services" do
    subject { build(:project) }
4580

4581 4582 4583
    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))
4584

4585
      services = subject.find_or_initialize_services
4586

4587 4588 4589 4590
      expect(services.count).to eq 1
      expect(services).to include(PushoverService)
    end
  end
4591

4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608
  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
4609
    end
4610 4611
  end

4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634
  describe '.find_without_deleted' do
    it 'returns nil if the project is about to be removed' do
      project = create(:project, pending_delete: true)

      expect(described_class.find_without_deleted(project.id)).to be_nil
    end

    it 'returns a project when it is not about to be removed' do
      project = create(:project)

      expect(described_class.find_without_deleted(project.id)).to eq(project)
    end
  end

  describe '.for_group' do
    it 'returns the projects for a given group' do
      group = create(:group)
      project = create(:project, namespace: group)

      expect(described_class.for_group(group)).to eq([project])
    end
  end

S
Shinya Maeda 已提交
4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668
  describe '.deployments' do
    subject { project.deployments }

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

    before do
      allow_any_instance_of(Deployment).to receive(:create_ref)
    end

    context 'when there is a deployment record with created status' do
      let(:deployment) { create(:deployment, :created, project: project) }

      it 'does not return the record' do
        is_expected.to be_empty
      end
    end

    context 'when there is a deployment record with running status' do
      let(:deployment) { create(:deployment, :running, project: project) }

      it 'does not return the record' do
        is_expected.to be_empty
      end
    end

    context 'when there is a deployment record with success status' do
      let(:deployment) { create(:deployment, :success, project: project) }

      it 'returns the record' do
        is_expected.to eq([deployment])
      end
    end
  end

4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690
  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 已提交
4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711
  describe '#all_clusters' do
    let(:project) { create(:project) }
    let(:cluster) { create(:cluster, cluster_type: :project_type, projects: [project]) }

    subject { project.all_clusters }

    it 'returns project level cluster' do
      expect(subject).to eq([cluster])
    end

    context 'project belongs to a group' do
      let(:group_cluster) { create(:cluster, :group) }
      let(:group) { group_cluster.group }
      let(:project) { create(:project, group: group) }

      it 'returns clusters for groups of this project' do
        expect(subject).to contain_exactly(cluster, group_cluster)
      end
    end
  end

4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734
  describe '#object_pool_params' do
    let(:project) { create(:project, :repository, :public) }

    subject { project.object_pool_params }

    before do
      stub_application_setting(hashed_storage_enabled: true)
    end

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

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 4764 4765 4766 4767 4768 4769 4770 4771 4772
  describe '#git_objects_poolable?' do
    subject { project }

    context 'when the feature flag is turned off' do
      before do
        stub_feature_flags(object_pools: false)
      end

      let(:project) { create(:project, :repository, :public) }

      it { is_expected.not_to be_git_objects_poolable }
    end

    context 'when the feature flag is enabled' do
      context 'when not using hashed storage' do
        let(:project) { create(:project, :legacy_storage, :public, :repository) }

        it { is_expected.not_to be_git_objects_poolable }
      end

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

        it { is_expected.not_to be_git_objects_poolable }
      end

      context 'when objects are poolable' do
        let(:project) { create(:project, :repository, :public) }

        before do
          stub_application_setting(hashed_storage_enabled: true)
        end

        it { is_expected.to be_git_objects_poolable }
      end
    end
  end

4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783
  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

4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852
  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

4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867
  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

4868
  def rugged_config
4869
    rugged_repo(project.repository).config
4870
  end
4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884

  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 已提交
4885
end