diff options
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r-- | spec/models/project_spec.rb | 539 |
1 files changed, 520 insertions, 19 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 1f7c6a82b91..e8588975118 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -24,6 +24,7 @@ describe Project do it { is_expected.to have_one(:slack_service) } it { is_expected.to have_one(:microsoft_teams_service) } it { is_expected.to have_one(:mattermost_service) } + it { is_expected.to have_one(:packagist_service) } it { is_expected.to have_one(:pushover_service) } it { is_expected.to have_one(:asana_service) } it { is_expected.to have_many(:boards) } @@ -53,9 +54,11 @@ describe Project do it { is_expected.to have_one(:import_data).class_name('ProjectImportData') } it { is_expected.to have_one(:last_event).class_name('Event') } it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) } + it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') } it { is_expected.to have_many(:commit_statuses) } it { is_expected.to have_many(:pipelines) } it { is_expected.to have_many(:builds) } + it { is_expected.to have_many(:build_trace_section_names)} it { is_expected.to have_many(:runner_projects) } it { is_expected.to have_many(:runners) } it { is_expected.to have_many(:active_runners) } @@ -75,6 +78,7 @@ describe Project do it { is_expected.to have_many(:uploads).dependent(:destroy) } it { is_expected.to have_many(:pipeline_schedules) } it { is_expected.to have_many(:members_and_requesters) } + it { is_expected.to have_one(:cluster) } context 'after initialized' do it "has a project_feature" do @@ -407,21 +411,23 @@ describe Project do end end - describe '#repository_storage_path' do - let(:project) { create(:project, repository_storage: 'custom') } - - before do - FileUtils.mkdir('tmp/tests/custom_repositories') - storages = { 'custom' => { 'path' => 'tmp/tests/custom_repositories' } } - allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) + describe '#merge_method' do + it 'returns "ff" merge_method when ff is enabled' do + project = build(:project, merge_requests_ff_only_enabled: true) + expect(project.merge_method).to be :ff end - after do - FileUtils.rm_rf('tmp/tests/custom_repositories') + it 'returns "merge" merge_method when ff is disabled' do + project = build(:project, merge_requests_ff_only_enabled: false) + expect(project.merge_method).to be :merge end + end + + describe '#repository_storage_path' do + let(:project) { create(:project) } it 'returns the repository storage path' do - expect(project.repository_storage_path).to eq('tmp/tests/custom_repositories') + expect(Dir.exist?(project.repository_storage_path)).to be(true) end end @@ -688,6 +694,44 @@ describe Project do project.cache_has_external_issue_tracker end.to change { project.has_external_issue_tracker}.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_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 end describe '#has_wiki?' do @@ -831,7 +875,7 @@ describe Project do let(:project) { create(:project) } context 'when avatar file is uploaded' do - let(:project) { create(:project, :with_avatar) } + let(:project) { create(:project, :public, :with_avatar) } let(:avatar_path) { "/uploads/-/system/project/avatar/#{project.id}/dk.png" } let(:gitlab_host) { "http://#{Gitlab.config.gitlab.host}" } @@ -1302,7 +1346,7 @@ describe Project do context 'using a regular repository' do it 'creates the repository' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(true) expect(project.repository).to receive(:after_create) @@ -1312,7 +1356,7 @@ describe Project do it 'adds an error if the repository could not be created' do expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(false) expect(project.repository).not_to receive(:after_create) @@ -1369,7 +1413,7 @@ describe Project do .and_return(false) expect(shell).to receive(:add_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(true) project.ensure_repository @@ -1718,6 +1762,21 @@ describe Project do it { expect(project.gitea_import?).to be true } end + describe '#ancestors_upto', :nested_groups do + let(:parent) { create(:group) } + let(:child) { create(:group, parent: parent) } + let(:child2) { create(:group, parent: child) } + let(:project) { create(:project, namespace: child2) } + + it 'returns all ancestors when no namespace is given' do + expect(project.ancestors_upto).to contain_exactly(child2, child, parent) + end + + it 'includes ancestors upto but excluding the given ancestor' do + expect(project.ancestors_upto(parent)).to contain_exactly(child2, child) + end + end + describe '#lfs_enabled?' do let(:project) { create(:project) } @@ -1813,6 +1872,73 @@ describe Project do end end + context 'forks' do + include ProjectForksHelper + + let(:project) { create(:project, :public) } + let!(:forked_project) { fork_project(project) } + + describe '#fork_network' do + it 'includes a fork of the project' do + expect(project.fork_network.projects).to include(forked_project) + end + + it 'includes a fork of a fork' do + other_fork = fork_project(forked_project) + + expect(project.fork_network.projects).to include(other_fork) + end + + it 'includes sibling forks' do + other_fork = fork_project(project) + + expect(forked_project.fork_network.projects).to include(other_fork) + end + + it 'includes the base project' do + expect(forked_project.fork_network.projects).to include(project.reload) + 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 + + describe '#fork_source' do + let!(:second_fork) { fork_project(forked_project) } + + it 'returns the direct source if it exists' do + expect(second_fork.fork_source).to eq(forked_project) + end + + it 'returns the root of the fork network when the directs source was deleted' do + forked_project.destroy + + expect(second_fork.fork_source).to eq(project) + end + end + end + describe '#pushes_since_gc' do let(:project) { create(:project) } @@ -2082,6 +2208,12 @@ describe Project do it { expect(project.parent).to eq(project.namespace) } end + describe '#parent_id' do + let(:project) { create(:project) } + + it { expect(project.parent_id).to eq(project.namespace_id) } + end + describe '#parent_changed?' do let(:project) { create(:project) } @@ -2335,6 +2467,7 @@ describe Project do context 'legacy storage' do let(:project) { create(:project, :repository) } let(:gitlab_shell) { Gitlab::Shell.new } + let(:project_storage) { project.send(:storage) } before do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) @@ -2362,10 +2495,22 @@ describe Project do describe '#legacy_storage?' do it 'returns true when storage_version is nil' do - project = build(:project) + project = build(:project, storage_version: nil) expect(project.legacy_storage?).to be_truthy end + + it 'returns true when the storage_version is 0' do + project = build(:project, storage_version: 0) + + expect(project.legacy_storage?).to be_truthy + end + end + + describe '#hashed_storage?' do + it 'returns false' do + expect(project.hashed_storage?(:repository)).to be_falsey + end end describe '#rename_repo' do @@ -2417,6 +2562,30 @@ describe Project do it { expect { subject }.to raise_error(StandardError) } end + + context 'gitlab pages' do + before do + expect(project_storage).to receive(:rename_repo) { true } + end + + it 'moves pages folder to new location' do + expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project) + + project.rename_repo + end + end + + context 'attachments' do + before do + expect(project_storage).to receive(:rename_repo) { true } + end + + it 'moves uploads folder to new location' do + expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project) + + project.rename_repo + end + end end describe '#pages_path' do @@ -2424,6 +2593,38 @@ describe Project do expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) end end + + describe '#migrate_to_hashed_storage!' do + it 'returns true' do + expect(project.migrate_to_hashed_storage!).to be_truthy + end + + it 'flags as read-only' do + expect { project.migrate_to_hashed_storage! }.to change { project.repository_read_only }.to(true) + end + + it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do + Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase + + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + + project.migrate_to_hashed_storage! + end + + it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the wiki repo is in use' do + Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: true)).increase + + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + + project.migrate_to_hashed_storage! + end + + it 'schedules ProjectMigrateHashedStorageWorker' do + expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).with(project.id) + + project.migrate_to_hashed_storage! + end + end end context 'hashed storage' do @@ -2437,6 +2638,24 @@ describe Project do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) end + describe '#legacy_storage?' do + it 'returns false' do + expect(project.legacy_storage?).to be_falsey + end + end + + describe '#hashed_storage?' do + it 'returns true if rolled out' do + expect(project.hashed_storage?(:attachments)).to be_truthy + end + + it 'returns false when not rolled out yet' do + project.storage_version = 1 + + expect(project.hashed_storage?(:attachments)).to be_falsey + end + end + describe '#base_dir' do it 'returns base_dir based on hash of project id' do expect(project.base_dir).to eq('@hashed/6b/86') @@ -2476,10 +2695,6 @@ describe Project do .to receive(:execute_hooks_for) .with(project, :rename) - expect_any_instance_of(Gitlab::UploadsTransfer) - .to receive(:rename_project) - .with('foo', project.path, project.namespace.full_path) - expect(project).to receive(:expire_caches_before_rename) expect(project).to receive(:expires_full_path_cache) @@ -2500,6 +2715,32 @@ describe Project do it { expect { subject }.to raise_error(StandardError) } end + + context 'gitlab pages' do + it 'moves pages folder to new location' do + expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project) + + project.rename_repo + end + end + + context 'attachments' do + it 'keeps uploads folder location unchanged' do + expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project) + + project.rename_repo + end + + context 'when not rolled out' do + let(:project) { create(:project, :repository, storage_version: 1) } + + it 'moves pages folder to new location' do + expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project) + + project.rename_repo + end + end + end end describe '#pages_path' do @@ -2507,5 +2748,265 @@ describe Project do expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path)) end end + + describe '#migrate_to_hashed_storage!' do + it 'returns nil' do + expect(project.migrate_to_hashed_storage!).to be_nil + end + + it 'does not flag as read-only' do + expect { project.migrate_to_hashed_storage! }.not_to change { project.repository_read_only } + end + end + end + + describe '#gl_repository' do + let(:project) { create(:project) } + + it 'delegates to Gitlab::GlRepository.gl_repository' do + expect(Gitlab::GlRepository).to receive(:gl_repository).with(project, true) + + project.gl_repository(is_wiki: true) + end + end + + describe '#has_ci?' do + set(:project) { create(:project) } + let(:repository) { double } + + before do + expect(project).to receive(:repository) { repository } + end + + context 'when has .gitlab-ci.yml' do + before do + expect(repository).to receive(:gitlab_ci_yml) { 'content' } + end + + it "CI is available" do + expect(project).to have_ci + end + end + + context 'when there is no .gitlab-ci.yml' do + before do + expect(repository).to receive(:gitlab_ci_yml) { nil } + end + + it "CI is not available" do + expect(project).not_to have_ci + end + + context 'when auto devops is enabled' do + before do + stub_application_setting(auto_devops_enabled: true) + end + + it "CI is available" do + expect(project).to have_ci + end + end + end + end + + describe '#auto_devops_enabled?' do + set(:project) { create(:project) } + + subject { project.auto_devops_enabled? } + + context 'when enabled in settings' do + before do + stub_application_setting(auto_devops_enabled: true) + end + + it 'auto devops is implicitly enabled' do + expect(project.auto_devops).to be_nil + expect(project).to be_auto_devops_enabled + end + + context 'when explicitly enabled' do + before do + create(:project_auto_devops, project: project) + end + + it "auto devops is enabled" do + expect(project).to be_auto_devops_enabled + end + end + + context 'when explicitly disabled' do + before do + create(:project_auto_devops, project: project, enabled: false) + end + + it "auto devops is disabled" do + expect(project).not_to be_auto_devops_enabled + end + end + end + + context 'when disabled in settings' do + before do + stub_application_setting(auto_devops_enabled: false) + end + + it 'auto devops is implicitly disabled' do + expect(project.auto_devops).to be_nil + expect(project).not_to be_auto_devops_enabled + end + + context 'when explicitly enabled' do + before do + create(:project_auto_devops, project: project) + end + + it "auto devops is enabled" do + expect(project).to be_auto_devops_enabled + end + end + end + end + + describe '#has_auto_devops_implicitly_disabled?' do + set(:project) { create(:project) } + + context 'when enabled in settings' do + before do + stub_application_setting(auto_devops_enabled: true) + end + + it 'does not have auto devops implicitly disabled' do + expect(project).not_to have_auto_devops_implicitly_disabled + end + end + + context 'when disabled in settings' do + before do + stub_application_setting(auto_devops_enabled: false) + end + + it 'auto devops is implicitly disabled' do + expect(project).to have_auto_devops_implicitly_disabled + end + + context 'when explicitly disabled' do + before do + create(:project_auto_devops, project: project, enabled: false) + end + + it 'does not have auto devops implicitly disabled' do + expect(project).not_to have_auto_devops_implicitly_disabled + end + end + + context 'when explicitly enabled' do + before do + create(:project_auto_devops, project: project) + end + + it 'does not have auto devops implicitly disabled' do + expect(project).not_to have_auto_devops_implicitly_disabled + end + end + end + end + + context '#auto_devops_variables' do + set(:project) { create(:project) } + + subject { project.auto_devops_variables } + + context 'when enabled in settings' do + before do + stub_application_setting(auto_devops_enabled: true) + end + + context 'when domain is empty' do + before do + create(:project_auto_devops, project: project, domain: nil) + end + + it 'variables are empty' do + is_expected.to be_empty + end + end + + context 'when domain is configured' do + before do + create(:project_auto_devops, project: project, domain: 'example.com') + end + + it "variables are not empty" do + is_expected.not_to be_empty + end + end + end + end + + describe '#latest_successful_builds_for' do + let(:project) { build(:project) } + + before do + allow(project).to receive(:default_branch).and_return('master') + end + + context 'without a ref' do + it 'returns a pipeline for the default branch' do + expect(project) + .to receive(:latest_successful_pipeline_for_default_branch) + + project.latest_successful_pipeline_for + end + end + + context 'with the ref set to the default branch' do + it 'returns a pipeline for the default branch' do + expect(project) + .to receive(:latest_successful_pipeline_for_default_branch) + + project.latest_successful_pipeline_for(project.default_branch) + end + end + + context 'with a ref that is not the default branch' do + it 'returns the latest successful pipeline for the given ref' do + expect(project.pipelines).to receive(:latest_successful_for).with('foo') + + project.latest_successful_pipeline_for('foo') + end + end + end + + 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 + + describe '#latest_successful_pipeline_for_default_branch' do + let(:project) { build(:project) } + + before do + allow(project).to receive(:default_branch).and_return('master') + end + + it 'memoizes and returns the latest successful pipeline for the default branch' do + pipeline = double(:pipeline) + + expect(project.pipelines).to receive(:latest_successful_for) + .with(project.default_branch) + .and_return(pipeline) + .once + + 2.times do + expect(project.latest_successful_pipeline_for_default_branch) + .to eq(pipeline) + end + end end end |