diff options
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r-- | spec/models/project_spec.rb | 638 |
1 files changed, 499 insertions, 139 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 4cf8d861595..a2f8fac2f38 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -63,7 +63,6 @@ describe Project do 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) } it { is_expected.to have_many(:variables) } it { is_expected.to have_many(:triggers) } it { is_expected.to have_many(:pages_domains) } @@ -77,13 +76,15 @@ describe Project do it { is_expected.to have_many(:project_group_links) } it { is_expected.to have_many(:notification_settings).dependent(:delete_all) } it { is_expected.to have_many(:forks).through(:forked_project_links) } - it { is_expected.to have_many(:uploads).dependent(:destroy) } + it { is_expected.to have_many(:uploads) } it { is_expected.to have_many(:pipeline_schedules) } it { is_expected.to have_many(:members_and_requesters) } it { is_expected.to have_many(:clusters) } it { is_expected.to have_many(:custom_attributes).class_name('ProjectCustomAttribute') } it { is_expected.to have_many(:project_badges).class_name('ProjectBadge') } it { is_expected.to have_many(:lfs_file_locks) } + it { is_expected.to have_many(:project_deploy_tokens) } + it { is_expected.to have_many(:deploy_tokens).through(:project_deploy_tokens) } context 'after initialized' do it "has a project_feature" do @@ -91,6 +92,23 @@ describe Project do end end + 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 + + context 'updating cd_cd_settings' do + it 'does not raise an error' do + project = create(:project) + + expect { project.update(ci_cd_settings: nil) }.not_to raise_exception + end + end + describe '#members & #requesters' do let(:project) { create(:project, :public, :access_requestable) } let(:requester) { create(:user) } @@ -220,18 +238,25 @@ describe Project do expect(project2.import_data).to be_nil end - it "does not allow blocked import_url localhost" do + it "does not allow import_url pointing to localhost" do project2 = build(:project, import_url: 'http://localhost:9000/t.git') expect(project2).to be_invalid - expect(project2.errors[:import_url]).to include('imports are not allowed from that URL') + expect(project2.errors[:import_url].first).to include('Requests to localhost are not allowed') end - it "does not allow blocked import_url port" do + it "does not allow import_url with invalid ports" do project2 = build(:project, import_url: 'http://github.com:25/t.git') expect(project2).to be_invalid - expect(project2.errors[:import_url]).to include('imports are not allowed from that URL') + expect(project2.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443') + end + + it "does not allow import_url with invalid user" do + project2 = build(:project, import_url: 'http://$user:password@github.com/t.git') + + expect(project2).to be_invalid + expect(project2.errors[:import_url].first).to include('Username needs to start with an alphanumeric character') end describe 'project pending deletion' do @@ -291,12 +316,12 @@ describe Project do describe 'project token' do it 'sets an random token if none provided' do - project = FactoryBot.create :project, runners_token: '' + project = FactoryBot.create(:project, runners_token: '') expect(project.runners_token).not_to eq('') end it 'does not set an random token if one provided' do - project = FactoryBot.create :project, runners_token: 'my-token' + project = FactoryBot.create(:project, runners_token: 'my-token') expect(project.runners_token).to eq('my-token') end end @@ -323,7 +348,7 @@ describe Project do let(:owner) { create(:user, name: 'Gitlab') } let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) } let(:project) { create(:project, path: 'sample-project', namespace: namespace) } - let(:group) { create(:group, name: 'Group', path: 'sample-group', owner: owner) } + let(:group) { create(:group, name: 'Group', path: 'sample-group') } context 'when nil argument' do it 'returns nil' do @@ -438,14 +463,6 @@ describe Project do end end - describe '#repository_storage_path' do - let(:project) { create(:project) } - - it 'returns the repository storage path' do - expect(Dir.exist?(project.repository_storage_path)).to be(true) - end - end - it 'returns valid url to repo' do project = described_class.new(path: 'somewhere') expect(project.url_to_repo).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + 'somewhere.git') @@ -459,6 +476,34 @@ describe Project do end end + describe "#readme_url" do + let(:project) { create(:project, :repository, path: "somewhere") } + + context 'with a non-existing repository' do + it 'returns nil' do + allow(project.repository).to receive(:tree).with(:head).and_return(nil) + + expect(project.readme_url).to be_nil + end + end + + context 'with an existing repository' do + context 'when no README exists' do + it 'returns nil' do + allow_any_instance_of(Tree).to receive(:readme).and_return(nil) + + expect(project.readme_url).to be_nil + end + end + + context 'when a README exists' do + it 'returns the README' do + expect(project.readme_url).to eql("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere/blob/master/README.md") + end + end + end + end + describe "#new_issuable_address" do let(:project) { create(:project, path: "somewhere") } let(:user) { create(:user) } @@ -629,7 +674,7 @@ describe Project do describe '#to_param' do context 'with namespace' do before do - @group = create :group, name: 'gitlab' + @group = create(:group, name: 'gitlab') @project = create(:project, name: 'gitlabhq', namespace: @group) end @@ -856,8 +901,8 @@ describe Project do describe '#star_count' do it 'counts stars from multiple users' do - user1 = create :user - user2 = create :user + user1 = create(:user) + user2 = create(:user) project = create(:project, :public) expect(project.star_count).to eq(0) @@ -879,7 +924,7 @@ describe Project do end it 'counts stars on the right project' do - user = create :user + user = create(:user) project1 = create(:project, :public) project2 = create(:project, :public) @@ -922,7 +967,7 @@ describe Project do it 'is false if avatar is html page' do project.update_attribute(:avatar, 'uploads/avatar.html') - expect(project.avatar_type).to eq(['only images allowed']) + 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']) end end @@ -1097,12 +1142,12 @@ describe Project do end context 'repository storage by default' do - let(:project) { create(:project) } + let(:project) { build(:project) } before do storages = { - 'default' => { 'path' => 'tmp/tests/repositories' }, - 'picked' => { 'path' => 'tmp/tests/repositories' } + 'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'), + 'picked' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories') } allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) end @@ -1136,51 +1181,112 @@ describe Project do end end - describe '#any_runners' do - let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) } - let(:specific_runner) { create(:ci_runner) } - let(:shared_runner) { create(:ci_runner, :shared) } + describe '#any_runners?' do + context 'shared runners' do + let(:project) { create(:project, shared_runners_enabled: shared_runners_enabled) } + let(:specific_runner) { create(:ci_runner, :project, projects: [project]) } + let(:shared_runner) { create(:ci_runner, :instance) } - context 'for shared runners disabled' do - let(:shared_runners_enabled) { false } + context 'for shared runners disabled' do + let(:shared_runners_enabled) { false } - it 'has no runners available' do - expect(project.any_runners?).to be_falsey - end + it 'has no runners available' do + expect(project.any_runners?).to be_falsey + end - it 'has a specific runner' do - project.runners << specific_runner - expect(project.any_runners?).to be_truthy - end + it 'has a specific runner' do + specific_runner + + expect(project.any_runners?).to be_truthy + end - it 'has a shared runner, but they are prohibited to use' do - shared_runner - expect(project.any_runners?).to be_falsey + it 'has a shared runner, but they are prohibited to use' do + shared_runner + + expect(project.any_runners?).to be_falsey + end + + it 'checks the presence of specific runner' do + specific_runner + + expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy + end + + it 'returns false if match cannot be found' do + specific_runner + + expect(project.any_runners? { false }).to be_falsey + end end - it 'checks the presence of specific runner' do - project.runners << specific_runner - expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy + context 'for shared runners enabled' do + let(:shared_runners_enabled) { true } + + it 'has a shared runner' do + shared_runner + + expect(project.any_runners?).to be_truthy + end + + it 'checks the presence of shared runner' do + shared_runner + + expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy + end + + it 'returns false if match cannot be found' do + shared_runner + + expect(project.any_runners? { false }).to be_falsey + end end end - context 'for shared runners enabled' do - let(:shared_runners_enabled) { true } + context 'group runners' do + let(:project) { create(:project, group_runners_enabled: group_runners_enabled) } + let(:group) { create(:group, projects: [project]) } + let(:group_runner) { create(:ci_runner, :group, groups: [group]) } - it 'has a shared runner' do - shared_runner - expect(project.any_runners?).to be_truthy + context 'for group runners disabled' do + let(:group_runners_enabled) { false } + + 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 + + expect(project.any_runners?).to be_falsey + end end - it 'checks the presence of shared runner' do - shared_runner - expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy + context 'for group runners enabled' do + let(:group_runners_enabled) { true } + + it 'has a group runner' do + group_runner + + expect(project.any_runners?).to be_truthy + end + + it 'checks the presence of group runner' do + group_runner + + expect(project.any_runners? { |runner| runner == group_runner }).to be_truthy + end + + it 'returns false if match cannot be found' do + group_runner + + expect(project.any_runners? { false }).to be_falsey + end end end end describe '#shared_runners' do - let!(:runner) { create(:ci_runner, :shared) } + let!(:runner) { create(:ci_runner, :instance) } subject { project.shared_runners } @@ -1221,7 +1327,7 @@ describe Project do end describe '#pages_deployed?' do - let(:project) { create :project } + let(:project) { create(:project) } subject { project.pages_deployed? } @@ -1239,8 +1345,8 @@ describe Project do end describe '#pages_url' do - let(:group) { create :group, name: group_name } - let(:project) { create :project, namespace: group, name: project_name } + let(:group) { create(:group, name: group_name) } + let(:project) { create(:project, namespace: group, name: project_name) } let(:domain) { 'Example.com' } subject { project.pages_url } @@ -1265,6 +1371,34 @@ describe Project do end end + describe '#pages_group_url' do + let(:group) { create(:group, name: group_name) } + let(:project) { create(:project, namespace: group, name: project_name) } + 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}") } + end + end + describe '.search' do let(:project) { create(:project, description: 'kitten mittens') } @@ -1356,8 +1490,8 @@ describe Project do let(:private_group) { create(:group, visibility_level: 0) } let(:internal_group) { create(:group, visibility_level: 10) } - let(:private_project) { create :project, :private, group: private_group } - let(:internal_project) { create :project, :internal, group: internal_group } + let(:private_project) { create(:project, :private, group: private_group) } + let(:internal_project) { create(:project, :internal, group: internal_group) } context 'when group is private project can not be internal' do it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey } @@ -1422,7 +1556,7 @@ describe Project do .and_return(false) allow(shell).to receive(:create_repository) - .with(project.repository_storage_path, project.disk_path) + .with(project.repository_storage, project.disk_path) .and_return(true) expect(project).to receive(:create_repository).with(force: true) @@ -1453,52 +1587,6 @@ describe Project do end end - describe '#user_can_push_to_empty_repo?' do - let(:project) { create(:project) } - let(:user) { create(:user) } - - it 'returns false when default_branch_protection is in full protection and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - - it 'returns false when default_branch_protection only lets devs merge and user is dev' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - - it 'returns true when default_branch_protection lets devs push and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns true when default_branch_protection is unprotected and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns true when user is master' do - project.add_master(user) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns false when the repo is not empty' do - project.add_master(user) - expect(project).to receive(:empty_repo?).and_return(false) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - end - describe '#container_registry_url' do let(:project) { create(:project) } @@ -1612,15 +1700,44 @@ describe Project do end end + describe '#human_import_status_name' do + context 'when import_state exists' do + it 'returns the humanized status name' do + project = create(:project) + create(:import_state, :started, project: project) + + expect(project.human_import_status_name).to eq("started") + end + end + + context 'when import_state was not created yet' do + let(:project) { create(:project, :import_started) } + + it 'ensures import_state is created and returns humanized status name' do + expect do + project.human_import_status_name + end.to change { ProjectImportState.count }.from(0).to(1) + end + + it 'returns humanized status name' do + expect(project.human_import_status_name).to eq("started") + end + end + end + describe 'Project import job' do let(:project) { create(:project, import_url: generate(:url)) } before do allow_any_instance_of(Gitlab::Shell).to receive(:import_repository) - .with(project.repository_storage_path, project.disk_path, project.import_url) + .with(project.repository_storage, project.disk_path, project.import_url) .and_return(true) - expect_any_instance_of(Repository).to receive(:after_import) + # Works around https://github.com/rspec/rspec-mocks/issues/910 + allow(described_class).to receive(:find).with(project.id).and_return(project) + expect(project.repository).to receive(:after_import) + .and_call_original + expect(project.wiki.repository).to receive(:after_import) .and_call_original end @@ -1650,7 +1767,8 @@ describe Project do it 'resets project import_error' do error_message = 'Some error' - mirror = create(:project_empty_repo, :import_started, import_error: error_message) + mirror = create(:project_empty_repo, :import_started) + mirror.import_state.update_attributes(last_error: error_message) expect { mirror.import_finish }.to change { mirror.import_error }.from(error_message).to(nil) end @@ -1770,10 +1888,7 @@ describe Project do let(:project) { forked_project_link.forked_to_project } it 'schedules a RepositoryForkWorker job' do - expect(RepositoryForkWorker).to receive(:perform_async).with( - project.id, - forked_from_project.repository_storage_path, - forked_from_project.disk_path).and_return(import_jid) + expect(RepositoryForkWorker).to receive(:perform_async).with(project.id).and_return(import_jid) expect(project.add_import_job).to eq(import_jid) end @@ -1801,6 +1916,83 @@ describe Project do it { expect(project.gitea_import?).to be true } end + describe '#has_remote_mirror?' do + 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 + project.remote_mirrors.first.update_attributes(enabled: false) + + 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 + project.remote_mirrors.first.update_attributes(enabled: false) + + 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 + describe '#ancestors_upto', :nested_groups do let(:parent) { create(:group) } let(:child) { create(:group, parent: parent) } @@ -1997,6 +2189,22 @@ describe Project do expect(forked_project.lfs_storage_project).to eq forked_project end end + + 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 end describe '#pushes_since_gc' do @@ -2131,6 +2339,22 @@ describe Project do end end + describe '#any_lfs_file_locks?', :request_store do + set(:project) { create(:project) } + + 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 + describe '#protected_for?' do let(:project) { create(:project) } @@ -2306,8 +2530,8 @@ describe Project do end describe '#pages_url' do - let(:group) { create :group, name: 'Group' } - let(:nested_group) { create :group, parent: group } + let(:group) { create(:group, name: 'Group') } + let(:nested_group) { create(:group, parent: group) } let(:domain) { 'Example.com' } subject { project.pages_url } @@ -2318,7 +2542,7 @@ describe Project do end context 'top-level group' do - let(:project) { create :project, namespace: group, name: project_name } + let(:project) { create(:project, namespace: group, name: project_name) } context 'group page' do let(:project_name) { 'group.example.com' } @@ -2334,7 +2558,7 @@ describe Project do end context 'nested group' do - let(:project) { create :project, namespace: nested_group, name: project_name } + let(:project) { create(:project, namespace: nested_group, name: project_name) } let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" } context 'group page' do @@ -2352,7 +2576,7 @@ describe Project do end describe '#http_url_to_repo' do - let(:project) { create :project } + let(:project) { create(:project) } it 'returns the url to the repo without a username' do expect(project.http_url_to_repo).to eq("#{project.web_url}.git") @@ -2532,7 +2756,7 @@ describe Project do end end - describe '#remove_exports' do + describe '#remove_export' do let(:legacy_project) { create(:project, :legacy_storage, :with_export) } let(:project) { create(:project, :with_export) } @@ -2580,6 +2804,23 @@ describe Project do end end + describe '#remove_exported_project_file' do + let(:project) { create(:project, :with_export) } + + it 'removes the exported project file' do + exported_file = project.export_project_path + + expect(File.exist?(exported_file)).to be_truthy + + allow(FileUtils).to receive(:rm_f).and_call_original + expect(FileUtils).to receive(:rm_f).with(exported_file).and_call_original + + project.remove_exported_project_file + + expect(File.exist?(exported_file)).to be_falsy + end + end + describe '#forks_count' do it 'returns the number of forks' do project = build(:project) @@ -2613,7 +2854,7 @@ describe Project do describe '#ensure_storage_path_exists' do it 'delegates to gitlab_shell to ensure namespace is created' do - expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, project.base_dir) + expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, project.base_dir) project.ensure_storage_path_exists end @@ -2652,12 +2893,12 @@ describe Project do expect(gitlab_shell).to receive(:mv_repository) .ordered - .with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}") + .with(project.repository_storage, "#{project.namespace.full_path}/foo", "#{project.full_path}") .and_return(true) expect(gitlab_shell).to receive(:mv_repository) .ordered - .with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki") + .with(project.repository_storage, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki") .and_return(true) expect_any_instance_of(SystemHooksService) @@ -2718,7 +2959,7 @@ describe Project do project.rename_repo - expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path) + expect(rugged_config['gitlab.fullpath']).to eq(project.full_path) end end @@ -2806,7 +3047,7 @@ describe Project do it 'delegates to gitlab_shell to ensure namespace is created' do allow(project).to receive(:gitlab_shell).and_return(gitlab_shell) - expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, hashed_prefix) + expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, hashed_prefix) project.ensure_storage_path_exists end @@ -2879,7 +3120,7 @@ describe Project do it 'updates project full path in .git/config' do project.rename_repo - expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path) + expect(rugged_config['gitlab.fullpath']).to eq(project.full_path) end end @@ -3177,14 +3418,16 @@ describe Project do end describe '#after_import' do - let(:project) { build(:project) } + let(:project) { create(:project) } it 'runs the correct hooks' do expect(project.repository).to receive(:after_import) + expect(project.wiki.repository).to receive(:after_import) expect(project).to receive(:import_finish) expect(project).to receive(:update_project_counter_caches) expect(project).to receive(:remove_import_jid) expect(project).to receive(:after_create_default_branch) + expect(project).to receive(:refresh_markdown_cache!) project.after_import end @@ -3263,7 +3506,8 @@ describe Project do context 'with an import JID' do it 'unsets the import JID' do - project = create(:project, import_jid: '123') + project = create(:project) + create(:import_state, project: project, jid: '123') expect(Gitlab::SidekiqStatus) .to receive(:unset) @@ -3297,13 +3541,13 @@ describe Project do it 'writes full path in .git/config when key is missing' do project.write_repository_config - expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path + expect(rugged_config['gitlab.fullpath']).to eq project.full_path end it 'updates full path in .git/config when key is present' do project.write_repository_config(gl_full_path: 'old/path') - expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path) + expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path) end it 'does not raise an error with an empty repository' do @@ -3392,7 +3636,7 @@ describe Project do target_branch: 'target-branch', source_project: project, source_branch: 'awesome-feature-1', - allow_maintainer_to_push: true + allow_collaboration: true ) end @@ -3429,9 +3673,14 @@ describe Project do end end - describe '#branch_allows_maintainer_push?' do + describe '#branch_allows_collaboration_push?' do it 'allows access if the user can merge the merge request' do - expect(project.branch_allows_maintainer_push?(user, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(user, 'awesome-feature-1')) + .to be_truthy + end + + it 'allows access when there are merge requests open but no branch name is given' do + expect(project.branch_allows_collaboration?(user, nil)) .to be_truthy end @@ -3439,7 +3688,7 @@ describe Project do guest = create(:user) target_project.add_guest(guest) - expect(project.branch_allows_maintainer_push?(guest, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(guest, 'awesome-feature-1')) .to be_falsy end @@ -3449,34 +3698,145 @@ describe Project do target_branch: 'target-branch', source_project: project, source_branch: 'rejected-feature-1', - allow_maintainer_to_push: true) + allow_collaboration: true) - expect(project.branch_allows_maintainer_push?(user, 'rejected-feature-1')) + expect(project.branch_allows_collaboration?(user, 'rejected-feature-1')) .to be_falsy end it 'does not allow access if the user cannot merge the merge request' do create(:protected_branch, :masters_can_push, project: target_project, name: 'target-branch') - expect(project.branch_allows_maintainer_push?(user, 'awesome-feature-1')) + expect(project.branch_allows_collaboration?(user, 'awesome-feature-1')) .to be_falsy end it 'caches the result' do - control = ActiveRecord::QueryRecorder.new { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } + control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') } - expect { 3.times { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } } + expect { 3.times { project.branch_allows_collaboration?(user, 'awesome-feature-1') } } .not_to exceed_query_limit(control) end context 'when the requeststore is active', :request_store do it 'only queries per project across instances' do - control = ActiveRecord::QueryRecorder.new { project.branch_allows_maintainer_push?(user, 'awesome-feature-1') } + control = ActiveRecord::QueryRecorder.new { project.branch_allows_collaboration?(user, 'awesome-feature-1') } - expect { 2.times { described_class.find(project.id).branch_allows_maintainer_push?(user, 'awesome-feature-1') } } + expect { 2.times { described_class.find(project.id).branch_allows_collaboration?(user, 'awesome-feature-1') } } .not_to exceed_query_limit(control).with_threshold(2) end end end end + + 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 + + describe '#toggle_ci_cd_settings!' do + it 'toggles the value on #settings' do + project = create(:project, group_runners_enabled: false) + + expect(project.group_runners_enabled).to be false + + project.toggle_ci_cd_settings!(:group_runners_enabled) + + expect(project.group_runners_enabled).to be true + end + end + + 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 + + context 'when there is a gitlab deploy token associated but it is expired' do + let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) } + + it { is_expected.to be_nil } + end + + context 'when there is a deploy token associated with a different name' do + let!(:deploy_token) { create(:deploy_token, projects: [project]) } + + it { is_expected.to be_nil } + end + + 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 + end + + context 'with uploads' do + it_behaves_like 'model with mounted uploader', true do + let(:model_object) { create(:project, :with_avatar) } + let(:upload_attribute) { :avatar } + let(:uploader_class) { AttachmentUploader } + end + end + + def rugged_config + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + project.repository.rugged.config + end + end end |