diff options
Diffstat (limited to 'spec/lib')
40 files changed, 581 insertions, 482 deletions
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb index 9a2e521fdcf..919825a6102 100644 --- a/spec/lib/banzai/filter/redactor_filter_spec.rb +++ b/spec/lib/banzai/filter/redactor_filter_spec.rb @@ -46,7 +46,7 @@ describe Banzai::Filter::RedactorFilter do it 'allows permitted Project references' do user = create(:user) project = create(:project) - project.add_master(user) + project.add_maintainer(user) link = reference_link(project: project.id, reference_type: 'test') doc = filter(link, current_user: user) diff --git a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb index 1b3df7b20d4..64c994a268f 100644 --- a/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb +++ b/spec/lib/gitlab/background_migration/delete_diff_files_spec.rb @@ -1,31 +1,35 @@ require 'spec_helper' -describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180626125654 do +describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180619121030 do describe '#perform' do context 'when diff files can be deleted' do let(:merge_request) { create(:merge_request, :merged) } - let(:merge_request_diff) do + let!(:merge_request_diff) do merge_request.create_merge_request_diff merge_request.merge_request_diffs.first end + let(:perform) do + described_class.new.perform(MergeRequestDiff.pluck(:id)) + end + it 'deletes all merge request diff files' do - expect { described_class.new.perform(merge_request_diff.id) } + expect { perform } .to change { merge_request_diff.merge_request_diff_files.count } .from(20).to(0) end it 'updates state to without_files' do - expect { described_class.new.perform(merge_request_diff.id) } + expect { perform } .to change { merge_request_diff.reload.state } .from('collected').to('without_files') end it 'rollsback if something goes wrong' do - expect(MergeRequestDiffFile).to receive_message_chain(:where, :delete_all) + expect(described_class::MergeRequestDiffFile).to receive_message_chain(:where, :delete_all) .and_raise - expect { described_class.new.perform(merge_request_diff.id) } + expect { perform } .to raise_error merge_request_diff.reload @@ -35,35 +39,35 @@ describe Gitlab::BackgroundMigration::DeleteDiffFiles, :migration, schema: 20180 end end - it 'deletes no merge request diff files when MR is not merged' do - merge_request = create(:merge_request, :opened) - merge_request.create_merge_request_diff - merge_request_diff = merge_request.merge_request_diffs.first - - expect { described_class.new.perform(merge_request_diff.id) } - .not_to change { merge_request_diff.merge_request_diff_files.count } - .from(20) - end - - it 'deletes no merge request diff files when diff is marked as "without_files"' do + it 'reschedules itself when should_wait_deadtuple_vacuum' do merge_request = create(:merge_request, :merged) - merge_request.create_merge_request_diff - merge_request_diff = merge_request.merge_request_diffs.first + first_diff = merge_request.merge_request_diff + second_diff = merge_request.create_merge_request_diff - merge_request_diff.clean! + Sidekiq::Testing.fake! do + worker = described_class.new + allow(worker).to receive(:should_wait_deadtuple_vacuum?) { true } - expect { described_class.new.perform(merge_request_diff.id) } - .not_to change { merge_request_diff.merge_request_diff_files.count } - .from(20) + worker.perform([first_diff.id, second_diff.id]) + + expect(described_class.name.demodulize).to be_scheduled_delayed_migration(5.minutes, [first_diff.id, second_diff.id]) + expect(BackgroundMigrationWorker.jobs.size).to eq(1) + end end + end - it 'deletes no merge request diff files when diff is the latest' do - merge_request = create(:merge_request, :merged) - merge_request_diff = merge_request.merge_request_diff + describe '#should_wait_deadtuple_vacuum?' do + it 'returns true when hitting merge_request_diff_files hits DEAD_TUPLES_THRESHOLD', :postgresql do + worker = described_class.new + threshold_query_result = [{ "n_dead_tup" => described_class::DEAD_TUPLES_THRESHOLD.to_s }] + normal_query_result = [{ "n_dead_tup" => '3' }] + + allow(worker) + .to receive(:execute_statement) + .with(/SELECT n_dead_tup */) + .and_return(threshold_query_result, normal_query_result) - expect { described_class.new.perform(merge_request_diff.id) } - .not_to change { merge_request_diff.merge_request_diff_files.count } - .from(20) + expect(worker.should_wait_deadtuple_vacuum?).to be(true) end end end diff --git a/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb new file mode 100644 index 00000000000..fb5093b0bd1 --- /dev/null +++ b/spec/lib/gitlab/background_migration/schedule_diff_files_deletion_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::ScheduleDiffFilesDeletion, :migration, schema: 20180619121030 do + describe '#perform' do + let(:merge_request_diffs) { table(:merge_request_diffs) } + let(:merge_requests) { table(:merge_requests) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + + before do + stub_const("#{described_class.name}::DIFF_BATCH_SIZE", 3) + + namespaces.create!(id: 1, name: 'gitlab', path: 'gitlab') + projects.create!(id: 1, namespace_id: 1, name: 'gitlab', path: 'gitlab') + + merge_requests.create!(id: 1, target_project_id: 1, source_project_id: 1, target_branch: 'feature', source_branch: 'master', state: 'merged') + + merge_request_diffs.create!(id: 1, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 2, merge_request_id: 1, state: 'empty') + merge_request_diffs.create!(id: 3, merge_request_id: 1, state: 'without_files') + merge_request_diffs.create!(id: 4, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 5, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 6, merge_request_id: 1, state: 'collected') + merge_request_diffs.create!(id: 7, merge_request_id: 1, state: 'collected') + + merge_requests.update(1, latest_merge_request_diff_id: 7) + end + + it 'correctly schedules diff file deletion workers' do + Sidekiq::Testing.fake! do + Timecop.freeze do + described_class.new.perform + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, [1, 4, 5]) + + expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, [6]) + + expect(BackgroundMigrationWorker.jobs.size).to eq(2) + end + end + end + end +end diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb index 1cb8143a9e9..7c04aa27971 100644 --- a/spec/lib/gitlab/checks/change_access_spec.rb +++ b/spec/lib/gitlab/checks/change_access_spec.rb @@ -54,7 +54,7 @@ describe Gitlab::Checks::ChangeAccess do context 'as maintainer' do before do - project.add_master(user) + project.add_maintainer(user) end context 'deletion' do @@ -144,7 +144,7 @@ describe Gitlab::Checks::ChangeAccess do context 'if the user is allowed to delete protected branches' do before do - project.add_master(user) + project.add_maintainer(user) end context 'through the web interface' do diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb index a973ccda8de..8ba56d73838 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb @@ -99,9 +99,9 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do end end - context 'when user is a master' do + context 'when user is a maintainer' do before do - project.add_master(user) + project.add_maintainer(user) end it { is_expected.to be_truthy } diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index e2bb378f663..02f8c4c114b 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Play do context 'when user can not push to the branch' do before do build.project.add_developer(user) - create(:protected_branch, :masters_can_push, + create(:protected_branch, :maintainers_can_push, name: build.ref, project: project) end diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index 6ec35f8da7e..bb2d0a2c75c 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::Ci::Status::Stage::Common do context 'when user has permission to read pipeline' do before do - project.add_master(user) + project.add_maintainer(user) end it 'has details' do diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 8d4862932b2..1f35d1e4880 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::ClosingIssueExtractor do before do project.add_developer(project.creator) project.add_developer(project2.creator) - project2.add_master(project.creator) + project2.add_maintainer(project.creator) end describe "#closed_by_message" do @@ -298,7 +298,7 @@ describe Gitlab::ClosingIssueExtractor do context 'with an external issue tracker reference' do it 'extracts the referenced issue' do jira_project = create(:jira_project, name: 'JIRA_EXT1') - jira_project.add_master(jira_project.creator) + jira_project.add_maintainer(jira_project.creator) jira_issue = ExternalIssue.new("#{jira_project.name}-1", project: jira_project) closing_issue_extractor = described_class.new(jira_project, jira_project.creator) message = "Resolve #{jira_issue.to_reference}" @@ -379,6 +379,20 @@ describe Gitlab::ClosingIssueExtractor do .to match_array([issue, other_issue, third_issue]) end + it 'allows non-comma-separated issue numbers in single line message' do + message = "Closes #{reference} #{reference2} #{reference3}" + + expect(subject.closed_by_message(message)) + .to match_array([issue, other_issue, third_issue]) + end + + it 'allows mixed comma-separated and non-comma-separated issue numbers in single line message' do + message = "Closes #{reference}, #{reference2} and #{reference3}" + + expect(subject.closed_by_message(message)) + .to match_array([issue, other_issue, third_issue]) + end + it 'fetches issues in multi-line message' do message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}" diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb index 6de4bd3dc7c..f670c7f6c75 100644 --- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb @@ -36,9 +36,9 @@ describe Gitlab::CycleAnalytics::Permissions do end end - context 'user is master' do + context 'user is maintainer' do before do - project.add_master(user) + project.add_maintainer(user) end it 'has permissions to issue stage' do diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index 6900c4189b7..034b89a46fa 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -178,77 +178,67 @@ describe Gitlab::Git::Blob, seed_helper: true do end describe '.batch' do - shared_examples 'loading blobs in batch' do - let(:blob_references) do - [ - [SeedRepo::Commit::ID, "files/ruby/popen.rb"], - [SeedRepo::Commit::ID, 'six'] - ] - end + let(:blob_references) do + [ + [SeedRepo::Commit::ID, "files/ruby/popen.rb"], + [SeedRepo::Commit::ID, 'six'] + ] + end - subject { described_class.batch(repository, blob_references) } + subject { described_class.batch(repository, blob_references) } - it { expect(subject.size).to eq(blob_references.size) } + it { expect(subject.size).to eq(blob_references.size) } - context 'first blob' do - let(:blob) { subject[0] } + context 'first blob' do + let(:blob) { subject[0] } - it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) } - it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) } - it { expect(blob.path).to eq("files/ruby/popen.rb") } - it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) } - it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) } - it { expect(blob.size).to eq(669) } - it { expect(blob.mode).to eq("100644") } - end + it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) } + it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) } + it { expect(blob.path).to eq("files/ruby/popen.rb") } + it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) } + it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) } + it { expect(blob.size).to eq(669) } + it { expect(blob.mode).to eq("100644") } + end - context 'second blob' do - let(:blob) { subject[1] } + context 'second blob' do + let(:blob) { subject[1] } - it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') } - it { expect(blob.data).to eq('') } - it 'does not mark the blob as binary' do - expect(blob).not_to be_binary - end + it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') } + it { expect(blob.data).to eq('') } + it 'does not mark the blob as binary' do + expect(blob).not_to be_binary end + end - context 'limiting' do - subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) } + context 'limiting' do + subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) } - context 'positive' do - let(:blob_size_limit) { 10 } + context 'positive' do + let(:blob_size_limit) { 10 } - it { expect(subject.first.data.size).to eq(10) } - end + it { expect(subject.first.data.size).to eq(10) } + end - context 'zero' do - let(:blob_size_limit) { 0 } + context 'zero' do + let(:blob_size_limit) { 0 } - it 'only loads the metadata' do - expect(subject.first.size).not_to be(0) - expect(subject.first.data).to eq('') - end + it 'only loads the metadata' do + expect(subject.first.size).not_to be(0) + expect(subject.first.data).to eq('') end + end - context 'negative' do - let(:blob_size_limit) { -1 } + context 'negative' do + let(:blob_size_limit) { -1 } - it 'ignores MAX_DATA_DISPLAY_SIZE' do - stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100) + it 'ignores MAX_DATA_DISPLAY_SIZE' do + stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100) - expect(subject.first.data.size).to eq(669) - end + expect(subject.first.data.size).to eq(669) end end end - - context 'when Gitaly list_blobs_by_sha_path feature is enabled' do - it_behaves_like 'loading blobs in batch' - end - - context 'when Gitaly list_blobs_by_sha_path feature is disabled', :disable_gitaly do - it_behaves_like 'loading blobs in batch' - end end describe '.batch_metadata' do @@ -294,58 +284,48 @@ describe Gitlab::Git::Blob, seed_helper: true do ) end - shared_examples 'fetching batch of LFS pointers' do - it 'returns a list of Gitlab::Git::Blob' do - blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id]) - - expect(blobs.count).to eq(1) - expect(blobs).to all( be_a(Gitlab::Git::Blob) ) - expect(blobs).to be_an(Array) - end - - it 'accepts blob IDs as a lazy enumerator' do - blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy) - - expect(blobs.count).to eq(1) - expect(blobs).to all( be_a(Gitlab::Git::Blob) ) - end + it 'returns a list of Gitlab::Git::Blob' do + blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id]) - it 'handles empty list of IDs gracefully' do - blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy) - blobs_2 = described_class.batch_lfs_pointers(repository, []) + expect(blobs.count).to eq(1) + expect(blobs).to all( be_a(Gitlab::Git::Blob) ) + expect(blobs).to be_an(Array) + end - expect(blobs_1).to eq([]) - expect(blobs_2).to eq([]) - end + it 'accepts blob IDs as a lazy enumerator' do + blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id].lazy) - it 'silently ignores tree objects' do - blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid]) + expect(blobs.count).to eq(1) + expect(blobs).to all( be_a(Gitlab::Git::Blob) ) + end - expect(blobs).to eq([]) - end + it 'handles empty list of IDs gracefully' do + blobs_1 = described_class.batch_lfs_pointers(repository, [].lazy) + blobs_2 = described_class.batch_lfs_pointers(repository, []) - it 'silently ignores non lfs objects' do - blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id]) + expect(blobs_1).to eq([]) + expect(blobs_2).to eq([]) + end - expect(blobs).to eq([]) - end + it 'silently ignores tree objects' do + blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid]) - it 'avoids loading large blobs into memory' do - # This line could call `lookup` on `repository`, so do here before mocking. - non_lfs_blob_id = non_lfs_blob.id + expect(blobs).to eq([]) + end - expect(repository).not_to receive(:lookup) + it 'silently ignores non lfs objects' do + blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id]) - described_class.batch_lfs_pointers(repository, [non_lfs_blob_id]) - end + expect(blobs).to eq([]) end - context 'when Gitaly batch_lfs_pointers is enabled' do - it_behaves_like 'fetching batch of LFS pointers' - end + it 'avoids loading large blobs into memory' do + # This line could call `lookup` on `repository`, so do here before mocking. + non_lfs_blob_id = non_lfs_blob.id + + expect(repository).not_to receive(:lookup) - context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do - it_behaves_like 'fetching batch of LFS pointers' + described_class.batch_lfs_pointers(repository, [non_lfs_blob_id]) end end diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index ec1a684cfbc..a8c5627e678 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -2,6 +2,11 @@ require "spec_helper" describe Gitlab::Git::Branch, seed_helper: true do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:rugged) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged + end + end subject { repository.branches } @@ -124,6 +129,7 @@ describe Gitlab::Git::Branch, seed_helper: true do it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) } def create_commit - repository.create_commit(params.merge(committer: committer.merge(time: Time.now))) + params[:message].delete!("\r") + Rugged::Commit.create(rugged, params.merge(committer: committer.merge(time: Time.now))) end end diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 3bb0b5be15b..11ab376ab8f 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -27,6 +27,7 @@ EOT too_large: false } + # TODO use a Gitaly diff object instead @rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths: [".gitmodules"]).patches.first @@ -266,8 +267,12 @@ EOT describe '#submodule?' do before do - commit = repository.lookup('5937ac0a7beb003549fc5fd26fc247adbce4a52e') - @diffs = commit.parents[0].diff(commit).patches + # TODO use a Gitaly diff object instead + rugged_commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + repository.rugged.rev_parse('5937ac0a7beb003549fc5fd26fc247adbce4a52e') + end + + @diffs = rugged_commit.parents[0].diff(rugged_commit).patches end it { expect(described_class.new(@diffs[0]).submodule?).to eq(false) } diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb index 16e6bd35449..e51b875be11 100644 --- a/spec/lib/gitlab/git/index_spec.rb +++ b/spec/lib/gitlab/git/index_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::Git::Index, seed_helper: true do let(:index) { described_class.new(repository) } before do - index.read_tree(repository.lookup('master').tree) + index.read_tree(lookup('master').tree) end around do |example| @@ -30,7 +30,7 @@ describe Gitlab::Git::Index, seed_helper: true do entry = index.get(options[:file_path]) expect(entry).not_to be_nil - expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + expect(lookup(entry[:oid]).content).to eq(options[:content]) end end @@ -54,7 +54,7 @@ describe Gitlab::Git::Index, seed_helper: true do index.create(options) entry = index.get(options[:file_path]) - expect(repository.lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content])) + expect(lookup(entry[:oid]).content).to eq(Base64.decode64(options[:content])) end end @@ -68,7 +68,7 @@ describe Gitlab::Git::Index, seed_helper: true do index.create(options) entry = index.get(options[:file_path]) - expect(repository.lookup(entry[:oid]).content).to eq("Hello,\nWorld") + expect(lookup(entry[:oid]).content).to eq("Hello,\nWorld") end end end @@ -135,7 +135,7 @@ describe Gitlab::Git::Index, seed_helper: true do entry = index.get(options[:file_path]) - expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + expect(lookup(entry[:oid]).content).to eq(options[:content]) end it 'preserves file mode' do @@ -190,7 +190,7 @@ describe Gitlab::Git::Index, seed_helper: true do entry = index.get(options[:file_path]) expect(entry).not_to be_nil - expect(repository.lookup(entry[:oid]).content).to eq(options[:content]) + expect(lookup(entry[:oid]).content).to eq(options[:content]) end it 'preserves file mode' do @@ -232,4 +232,8 @@ describe Gitlab::Git::Index, seed_helper: true do end end end + + def lookup(revision) + repository.rugged.rev_parse(revision) + end end diff --git a/spec/lib/gitlab/git/popen_spec.rb b/spec/lib/gitlab/git/popen_spec.rb index b033ede9062..074e66d2a5d 100644 --- a/spec/lib/gitlab/git/popen_spec.rb +++ b/spec/lib/gitlab/git/popen_spec.rb @@ -2,6 +2,9 @@ require 'spec_helper' describe 'Gitlab::Git::Popen' do let(:path) { Rails.root.join('tmp').to_s } + let(:test_string) { 'The quick brown fox jumped over the lazy dog' } + # The pipe buffer is typically 64K. This string is about 440K. + let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] } let(:klass) do Class.new(Object) do @@ -70,6 +73,15 @@ describe 'Gitlab::Git::Popen' do end end end + + context 'with a process that writes a lot of data to stderr' do + it 'returns zero' do + output, status = klass.new.popen(spew_command, path) + + expect(output).to include(test_string) + expect(status).to eq(0) + end + end end context 'popen_with_timeout' do @@ -85,6 +97,17 @@ describe 'Gitlab::Git::Popen' do it { expect(output).to include('tests') } end + context 'multi-line string' do + let(:test_string) { "this is 1 line\n2nd line\n3rd line\n" } + let(:result) { klass.new.popen_with_timeout(['echo', test_string], timeout, path) } + let(:output) { result.first } + let(:status) { result.last } + + it { expect(status).to be_zero } + # echo adds its own line + it { expect(output).to eq(test_string + "\n") } + end + context 'non-zero status' do let(:result) { klass.new.popen_with_timeout(%w(cat NOTHING), timeout, path) } let(:output) { result.first } @@ -110,6 +133,13 @@ describe 'Gitlab::Git::Popen' do it "handles processes that do not shutdown correctly" do expect { klass.new.popen_with_timeout(['bash', '-c', "trap -- '' SIGTERM; sleep 1000"], timeout, path) }.to raise_error(Timeout::Error) end + + it 'handles process that writes a lot of data to stderr' do + output, status = klass.new.popen_with_timeout(spew_command, timeout, path) + + expect(output).to include(test_string) + expect(status).to eq(0) + end end context 'timeout period' do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 4f8e6f29255..6480f6c407d 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -321,90 +321,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - context '#submodules' do - around do |example| - # TODO #submodules will be removed, has been migrated to gitaly - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - example.run - end - end - - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } - - context 'where repo has submodules' do - let(:submodules) { repository.send(:submodules, 'master') } - let(:submodule) { submodules.first } - - it { expect(submodules).to be_kind_of Hash } - it { expect(submodules.empty?).to be_falsey } - - it 'should have valid data' do - expect(submodule).to eq([ - "six", { - "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d", - "name" => "six", - "url" => "git://github.com/randx/six.git" - } - ]) - end - - it 'should handle nested submodules correctly' do - nested = submodules['nested/six'] - expect(nested['name']).to eq('nested/six') - expect(nested['url']).to eq('git://github.com/randx/six.git') - expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196') - end - - it 'should handle deeply nested submodules correctly' do - nested = submodules['deeper/nested/six'] - expect(nested['name']).to eq('deeper/nested/six') - expect(nested['url']).to eq('git://github.com/randx/six.git') - expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196') - end - - it 'should not have an entry for an invalid submodule' do - expect(submodules).not_to have_key('invalid/path') - end - - it 'should not have an entry for an uncommited submodule dir' do - submodules = repository.send(:submodules, 'fix-existing-submodule-dir') - expect(submodules).not_to have_key('submodule-existing-dir') - end - - it 'should handle tags correctly' do - submodules = repository.send(:submodules, 'v1.2.1') - - expect(submodules.first).to eq([ - "six", { - "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d", - "name" => "six", - "url" => "git://github.com/randx/six.git" - } - ]) - end - - it 'should not break on invalid syntax' do - allow(repository).to receive(:blob_content).and_return(<<-GITMODULES.strip_heredoc) - [submodule "six"] - path = six - url = git://github.com/randx/six.git - - [submodule] - foo = bar - GITMODULES - - expect(submodules).to have_key('six') - end - end - - context 'where repo doesn\'t have submodules' do - let(:submodules) { repository.send(:submodules, '6d39438') } - it 'should return an empty hash' do - expect(submodules).to be_empty - end - end - end - describe '#commit_count' do shared_examples 'simple commit counting' do it { expect(repository.commit_count("master")).to eq(25) } @@ -611,21 +527,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe "#remove_remote" do - before(:all) do - @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - @repo.remove_remote("expendable") - end - - it "should remove the remote" do - expect(@repo.rugged.remotes).not_to include("expendable") - end - - after(:all) do - ensure_seeds - end - end - describe "#remote_update" do before(:all) do @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') @@ -633,7 +534,9 @@ describe Gitlab::Git::Repository, seed_helper: true do end it "should add the remote" do - expect(@repo.rugged.remotes["expendable"].url).to( + rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access { @repo.rugged } + + expect(rugged.remotes["expendable"].url).to( eq(TEST_NORMAL_REPO_PATH) ) end @@ -1157,6 +1060,13 @@ describe Gitlab::Git::Repository, seed_helper: true do @repo.rugged.config['core.autocrlf'] = true end + around do |example| + # OK because autocrlf is only used in gitaly-ruby + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + it 'return the value of the autocrlf option' do expect(@repo.autocrlf).to be(true) end @@ -1172,6 +1082,13 @@ describe Gitlab::Git::Repository, seed_helper: true do @repo.rugged.config['core.autocrlf'] = false end + around do |example| + # OK because autocrlf= is only used in gitaly-ruby + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + example.run + end + end + it 'should set the autocrlf option to the provided option' do @repo.autocrlf = :input @@ -1186,50 +1103,17 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#find_branch' do - shared_examples 'finding a branch' do - it 'should return a Branch for master' do - branch = repository.find_branch('master') + it 'should return a Branch for master' do + branch = repository.find_branch('master') - expect(branch).to be_a_kind_of(Gitlab::Git::Branch) - expect(branch.name).to eq('master') - end - - it 'should handle non-existent branch' do - branch = repository.find_branch('this-is-garbage') - - expect(branch).to eq(nil) - end + expect(branch).to be_a_kind_of(Gitlab::Git::Branch) + expect(branch.name).to eq('master') end - context 'when Gitaly find_branch feature is enabled' do - it_behaves_like 'finding a branch' - end - - context 'when Gitaly find_branch feature is disabled', :skip_gitaly_mock do - it_behaves_like 'finding a branch' - - context 'force_reload is true' do - it 'should reload Rugged::Repository' do - expect(Rugged::Repository).to receive(:new).twice.and_call_original - - repository.find_branch('master') - branch = repository.find_branch('master', force_reload: true) - - expect(branch).to be_a_kind_of(Gitlab::Git::Branch) - expect(branch.name).to eq('master') - end - end - - context 'force_reload is false' do - it 'should not reload Rugged::Repository' do - expect(Rugged::Repository).to receive(:new).once.and_call_original + it 'should handle non-existent branch' do + branch = repository.find_branch('this-is-garbage') - branch = repository.find_branch('master', force_reload: false) - - expect(branch).to be_a_kind_of(Gitlab::Git::Branch) - expect(branch.name).to eq('master') - end - end + expect(branch).to eq(nil) end end @@ -2042,54 +1926,61 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:repository) do Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') end + let(:rugged) do + Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } + end let(:remote_name) { 'my-remote' } + let(:url) { 'http://my-repo.git' } after do ensure_seeds end describe '#add_remote' do - let(:url) { 'http://my-repo.git' } let(:mirror_refmap) { '+refs/*:refs/*' } - it 'creates a new remote via Gitaly' do - expect_any_instance_of(Gitlab::GitalyClient::RemoteService) - .to receive(:add_remote).with(remote_name, url, mirror_refmap) + shared_examples 'add_remote' do + it 'added the remote' do + begin + rugged.remotes.delete(remote_name) + rescue Rugged::ConfigError + end - repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) + repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) + + expect(rugged.remotes[remote_name]).not_to be_nil + expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true') + expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true') + expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap) + end end - context 'with Gitaly disabled', :skip_gitaly_mock do - it 'creates a new remote via Rugged' do - expect_any_instance_of(Rugged::RemoteCollection).to receive(:create) - .with(remote_name, url) - expect_any_instance_of(Rugged::Config).to receive(:[]=) - .with("remote.#{remote_name}.mirror", true) - expect_any_instance_of(Rugged::Config).to receive(:[]=) - .with("remote.#{remote_name}.prune", true) - expect_any_instance_of(Rugged::Config).to receive(:[]=) - .with("remote.#{remote_name}.fetch", mirror_refmap) + context 'using Gitaly' do + it_behaves_like 'add_remote' + end - repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) - end + context 'with Gitaly disabled', :disable_gitaly do + it_behaves_like 'add_remote' end end describe '#remove_remote' do - it 'removes the remote via Gitaly' do - expect_any_instance_of(Gitlab::GitalyClient::RemoteService) - .to receive(:remove_remote).with(remote_name) + shared_examples 'remove_remote' do + it 'removes the remote' do + rugged.remotes.create(remote_name, url) - repository.remove_remote(remote_name) + repository.remove_remote(remote_name) + + expect(rugged.remotes[remote_name]).to be_nil + end end - context 'with Gitaly disabled', :skip_gitaly_mock do - it 'removes the remote via Rugged' do - expect_any_instance_of(Rugged::RemoteCollection).to receive(:delete) - .with(remote_name) + context 'using Gitaly' do + it_behaves_like 'remove_remote' + end - repository.remove_remote(remote_name) - end + context 'with Gitaly disabled', :disable_gitaly do + it_behaves_like 'remove_remote' end end end @@ -2281,20 +2172,25 @@ describe Gitlab::Git::Repository, seed_helper: true do let(:worktree_path) { File.join(repository_path, 'worktrees', 'delete-me') } it 'cleans up the files' do - repository.with_worktree(worktree_path, 'master', env: ENV) do - FileUtils.touch(worktree_path, mtime: Time.now - 8.hours) - # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object, - # but the HEAD must be 40 characters long or git will ignore it. - File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA) + create_worktree = %W[git -C #{repository_path} worktree add --detach #{worktree_path} master] + raise 'preparation failed' unless system(*create_worktree, err: '/dev/null') - # git 2.16 fails with "fatal: bad object HEAD" - expect { repository.rev_list(including: :all) }.to raise_error(Gitlab::Git::Repository::GitError) + FileUtils.touch(worktree_path, mtime: Time.now - 8.hours) + # git rev-list --all will fail in git 2.16 if HEAD is pointing to a non-existent object, + # but the HEAD must be 40 characters long or git will ignore it. + File.write(File.join(worktree_path, 'HEAD'), Gitlab::Git::BLANK_SHA) - repository.clean_stale_repository_files + # git 2.16 fails with "fatal: bad object HEAD" + expect(rev_list_all).to be false - expect { repository.rev_list(including: :all) }.not_to raise_error - expect(File.exist?(worktree_path)).to be_falsey - end + repository.clean_stale_repository_files + + expect(rev_list_all).to be true + expect(File.exist?(worktree_path)).to be_falsey + end + + def rev_list_all + system(*%W[git -C #{repository_path} rev-list --all], out: '/dev/null', err: '/dev/null') end it 'increments a counter upon an error' do diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb index b752c3e8341..d9d405e1ccc 100644 --- a/spec/lib/gitlab/git/rev_list_spec.rb +++ b/spec/lib/gitlab/git/rev_list_spec.rb @@ -32,65 +32,4 @@ describe Gitlab::Git::RevList do expect(rev_list.new_refs).to eq(%w[sha1 sha2]) end end - - context '#new_objects' do - it 'fetches list of newly pushed objects using rev-list' do - stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2") - - expect { |b| rev_list.new_objects(&b) }.to yield_with_args(%w[sha1 sha2]) - end - - it 'can skip pathless objects' do - stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file") - - expect { |b| rev_list.new_objects(require_path: true, &b) }.to yield_with_args(%w[sha2]) - end - - it 'can handle non utf-8 paths' do - non_utf_char = [0x89].pack("c*").force_encoding("UTF-8") - stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha2 πå†h/†ø/ƒîlé#{non_utf_char}\nsha1") - - rev_list.new_objects(require_path: true) do |object_ids| - expect(object_ids.force).to eq(%w[sha2]) - end - end - - it 'can yield a lazy enumerator' do - stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2") - - rev_list.new_objects do |object_ids| - expect(object_ids).to be_a Enumerator::Lazy - end - end - - it 'returns the result of the block when given' do - stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2") - - objects = rev_list.new_objects do |object_ids| - object_ids.first - end - - expect(objects).to eq 'sha1' - end - - it 'can accept list of references to exclude' do - stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2") - - expect { |b| rev_list.new_objects(not_in: ['master'], &b) }.to yield_with_args(%w[sha1 sha2]) - end - - it 'handles empty list of references to exclude as listing all known objects' do - stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2") - - expect { |b| rev_list.new_objects(not_in: [], &b) }.to yield_with_args(%w[sha1 sha2]) - end - end - - context '#all_objects' do - it 'fetches list of all pushed objects using rev-list' do - stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2") - - expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2]) - end - end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index ff32025253a..dbd64c4bec0 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -13,14 +13,6 @@ describe Gitlab::GitAccess do let(:authentication_abilities) { %i[read_project download_code push_code] } let(:redirected_path) { nil } let(:auth_result_type) { nil } - - let(:access) do - described_class.new(actor, project, - protocol, authentication_abilities: authentication_abilities, - namespace_path: namespace_path, project_path: project_path, - redirected_path: redirected_path, auth_result_type: auth_result_type) - end - let(:changes) { '_any' } let(:push_access_check) { access.check('git-receive-pack', changes) } let(:pull_access_check) { access.check('git-upload-pack', changes) } @@ -48,7 +40,7 @@ describe Gitlab::GitAccess do before do disable_protocol('http') - project.add_master(user) + project.add_maintainer(user) end it 'blocks http push and pull' do @@ -113,7 +105,7 @@ describe Gitlab::GitAccess do context 'when actor is a User' do context 'when the User can read the project' do before do - project.add_master(user) + project.add_maintainer(user) end it 'allows push and pull access' do @@ -254,7 +246,7 @@ describe Gitlab::GitAccess do shared_examples '#check with a key that is not valid' do before do - project.add_master(user) + project.add_maintainer(user) end context 'key is too small' do @@ -307,7 +299,7 @@ describe Gitlab::GitAccess do describe '#add_project_moved_message!', :clean_gitlab_redis_shared_state do before do - project.add_master(user) + project.add_maintainer(user) end context 'when a redirect was not followed to find the project' do @@ -335,7 +327,7 @@ describe Gitlab::GitAccess do describe '#check_authentication_abilities!' do before do - project.add_master(user) + project.add_maintainer(user) end context 'when download' do @@ -381,7 +373,7 @@ describe Gitlab::GitAccess do describe '#check_command_disabled!' do before do - project.add_master(user) + project.add_maintainer(user) end context 'over http' do @@ -529,8 +521,8 @@ describe Gitlab::GitAccess do end describe '#check_download_access!' do - it 'allows masters to pull' do - project.add_master(user) + it 'allows maintainers to pull' do + project.add_maintainer(user) expect { pull_access_check }.not_to raise_error end @@ -542,7 +534,7 @@ describe Gitlab::GitAccess do end it 'disallows blocked users to pull' do - project.add_master(user) + project.add_maintainer(user) user.block expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.') @@ -724,10 +716,11 @@ describe Gitlab::GitAccess do end describe '#check_push_access!' do + let(:unprotected_branch) { 'unprotected_branch' } + before do merge_into_protected_branch end - let(:unprotected_branch) { 'unprotected_branch' } let(:changes) do { push_new_branch: "#{Gitlab::Git::BLANK_SHA} 570e7b2ab refs/heads/wow", @@ -741,26 +734,18 @@ describe Gitlab::GitAccess do merge_into_protected_branch: "0b4bc9a #{merge_into_protected_branch} refs/heads/feature" } end - def stub_git_hooks - # Running the `pre-receive` hook is expensive, and not necessary for this test. - allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute) do |service, &block| - block.call(service) - end - end - def merge_into_protected_branch @protected_branch_merge_commit ||= begin Gitlab::GitalyClient::StorageSettings.allow_disk_access do - stub_git_hooks project.repository.add_branch(user, unprotected_branch, 'feature') - target_branch = project.repository.lookup('feature') + rugged = project.repository.rugged + target_branch = rugged.rev_parse('feature') source_branch = project.repository.create_file( user, 'filename', 'This is the file content', message: 'This is a good commit message', branch_name: unprotected_branch) - rugged = project.repository.rugged author = { email: "email@example.com", time: Time.now, name: "Example Git User" } merge_index = rugged.merge_commits(target_branch, source_branch) @@ -785,7 +770,7 @@ describe Gitlab::GitAccess do aggregate_failures do matrix.each do |action, allowed| - check = -> { access.send(:check_push_access!, changes[action]) } + check = -> { push_changes(changes[action]) } if allowed expect(&check).not_to raise_error, @@ -812,7 +797,7 @@ describe Gitlab::GitAccess do merge_into_protected_branch: true }, - master: { + maintainer: { push_new_branch: true, push_master: true, push_protected_branch: true, @@ -917,7 +902,7 @@ describe Gitlab::GitAccess do end run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }, - master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }, + maintainer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false }, admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false })) end end @@ -989,7 +974,7 @@ describe Gitlab::GitAccess do let(:project) { create(:project, :repository, :read_only) } it 'denies push access' do - project.add_master(user) + project.add_maintainer(user) expect { push_access_check }.to raise_unauthorized('The repository is temporarily read-only. Please try again later.') end @@ -1119,9 +1104,9 @@ describe Gitlab::GitAccess do it_behaves_like 'access after accepting terms' end - describe 'as a master of the project' do + describe 'as a maintainer of the project' do before do - project.add_master(user) + project.add_maintainer(user) end it_behaves_like 'access after accepting terms' @@ -1152,6 +1137,17 @@ describe Gitlab::GitAccess do private + def access + described_class.new(actor, project, protocol, + authentication_abilities: authentication_abilities, + namespace_path: namespace_path, project_path: project_path, + redirected_path: redirected_path, auth_result_type: auth_result_type) + end + + def push_changes(changes) + access.check('git-receive-pack', changes) + end + def raise_unauthorized(message) raise_error(Gitlab::GitAccess::UnauthorizedError, message) end diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 017facd0f5e..031f57dbc65 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::GoogleCodeImport::Importer do subject { described_class.new(project) } before do - project.add_master(project.creator) + project.add_maintainer(project.creator) project.create_import_data(data: import_data) end diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb index 2223f163177..90e6d653d34 100644 --- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb +++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb @@ -9,6 +9,7 @@ describe Gitlab::ImportExport::AvatarSaver do before do FileUtils.mkdir_p("#{shared.export_path}/avatar/") allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + stub_feature_flags(import_export_object_storage: false) end after do diff --git a/spec/lib/gitlab/import_export/members_mapper_spec.rb b/spec/lib/gitlab/import_export/members_mapper_spec.rb index 246f009ad27..67e4c289906 100644 --- a/spec/lib/gitlab/import_export/members_mapper_spec.rb +++ b/spec/lib/gitlab/import_export/members_mapper_spec.rb @@ -111,7 +111,7 @@ describe Gitlab::ImportExport::MembersMapper do end it 'maps the project member if it already exists' do - project.add_master(user2) + project.add_maintainer(user2) expect(members_mapper.map[exported_user_id]).to eq(user2.id) end diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 2b8a11ce8f9..fec8a2af9ab 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ImportExport::ProjectTreeSaver do let!(:project) { setup_project } before do - project.add_master(user) + project.add_maintainer(user) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(MergeRequest).to receive(:source_branch_sha).and_return('ABCD') allow_any_instance_of(MergeRequest).to receive(:target_branch_sha).and_return('DCBA') @@ -217,8 +217,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do expect(member_emails).not_to include('group@member.com') end - it 'does not export group members as master' do - Group.first.add_master(user) + it 'does not export group members as maintainer' do + Group.first.add_maintainer(user) expect(member_emails).not_to include('group@member.com') end diff --git a/spec/lib/gitlab/import_export/repo_saver_spec.rb b/spec/lib/gitlab/import_export/repo_saver_spec.rb index 187ec8fcfa2..5a646b4aac8 100644 --- a/spec/lib/gitlab/import_export/repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/repo_saver_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ImportExport::RepoSaver do let(:bundler) { described_class.new(project: project, shared: shared) } before do - project.add_master(user) + project.add_maintainer(user) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) end diff --git a/spec/lib/gitlab/import_export/uploads_manager_spec.rb b/spec/lib/gitlab/import_export/uploads_manager_spec.rb new file mode 100644 index 00000000000..9c3870a0af8 --- /dev/null +++ b/spec/lib/gitlab/import_export/uploads_manager_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Gitlab::ImportExport::UploadsManager do + let(:shared) { project.import_export_shared } + let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" } + let(:project) { create(:project) } + let(:exported_file_path) { "#{shared.export_path}/uploads/#{upload.secret}/#{File.basename(upload.path)}" } + + subject(:manager) { described_class.new(project: project, shared: shared) } + + before do + allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) + FileUtils.mkdir_p(shared.export_path) + end + + after do + FileUtils.rm_rf(shared.export_path) + end + + describe '#save' do + context 'when the project has uploads locally stored' do + let(:upload) { create(:upload, :issuable_upload, :with_file, model: project) } + + before do + project.uploads << upload + end + + it 'does not cause errors' do + manager.save + + expect(shared.errors).to be_empty + end + + it 'copies the file in the correct location when there is an upload' do + manager.save + + expect(File).to exist(exported_file_path) + end + end + + context 'using object storage' do + let!(:upload) { create(:upload, :issuable_upload, :object_storage, model: project) } + + before do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + end + + it 'saves the file' do + fake_uri = double + + expect(fake_uri).to receive(:open).and_return(StringIO.new('File content')) + expect(URI).to receive(:parse).and_return(fake_uri) + + manager.save + + expect(File.read(exported_file_path)).to eq('File content') + end + end + + describe '#restore' do + context 'using object storage' do + before do + stub_feature_flags(import_export_object_storage: true) + stub_uploads_object_storage(FileUploader) + + FileUtils.mkdir_p(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038')) + FileUtils.touch(File.join(shared.export_path, 'uploads/72a497a02fe3ee09edae2ed06d390038', "dummy.txt")) + end + + it 'restores the file' do + manager.restore + + expect(project.uploads.size).to eq(1) + expect(project.uploads.first.build_uploader.filename).to eq('dummy.txt') + end + end + end + end +end diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb index 095687fa89d..c716edd9397 100644 --- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb +++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb @@ -7,6 +7,7 @@ describe Gitlab::ImportExport::UploadsSaver do let(:shared) { project.import_export_shared } before do + stub_feature_flags(import_export_object_storage: false) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) end @@ -30,7 +31,7 @@ describe Gitlab::ImportExport::UploadsSaver do it 'copies the uploads to the export path' do saver.save - uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) } + uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) } expect(uploads).to include('banana_sample.gif') end @@ -52,7 +53,7 @@ describe Gitlab::ImportExport::UploadsSaver do it 'copies the uploads to the export path' do saver.save - uploads = Dir.glob(File.join(saver.uploads_export_path, '**/*')).map { |file| File.basename(file) } + uploads = Dir.glob(File.join(shared.export_path, 'uploads/**/*')).map { |file| File.basename(file) } expect(uploads).to include('banana_sample.gif') end diff --git a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb index 24bc231d5a0..441aa1defe6 100644 --- a/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb +++ b/spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::ImportExport::WikiRepoSaver do let!(:project_wiki) { ProjectWiki.new(project, user) } before do - project.add_master(user) + project.add_maintainer(user) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) project_wiki.wiki project_wiki.create_page("index", "test content") diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb index 10341486512..25827423914 100644 --- a/spec/lib/gitlab/import_sources_spec.rb +++ b/spec/lib/gitlab/import_sources_spec.rb @@ -12,7 +12,8 @@ describe Gitlab::ImportSources do 'FogBugz' => 'fogbugz', 'Repo by URL' => 'git', 'GitLab export' => 'gitlab_project', - 'Gitea' => 'gitea' + 'Gitea' => 'gitea', + 'Manifest file' => 'manifest' } expect(described_class.options).to eq(expected) @@ -31,6 +32,7 @@ describe Gitlab::ImportSources do git gitlab_project gitea + manifest ) expect(described_class.values).to eq(expected) @@ -63,7 +65,8 @@ describe Gitlab::ImportSources do 'fogbugz' => Gitlab::FogbugzImport::Importer, 'git' => nil, 'gitlab_project' => Gitlab::ImportExport::Importer, - 'gitea' => Gitlab::LegacyGithubImport::Importer + 'gitea' => Gitlab::LegacyGithubImport::Importer, + 'manifest' => nil } import_sources.each do |name, klass| @@ -82,7 +85,8 @@ describe Gitlab::ImportSources do 'fogbugz' => 'FogBugz', 'git' => 'Repo by URL', 'gitlab_project' => 'GitLab export', - 'gitea' => 'Gitea' + 'gitea' => 'Gitea', + 'manifest' => 'Manifest file' } import_sources.each do |name, title| diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb index 34b33772578..5c03a2ce7d3 100644 --- a/spec/lib/gitlab/kubernetes_spec.rb +++ b/spec/lib/gitlab/kubernetes_spec.rb @@ -70,4 +70,19 @@ describe Gitlab::Kubernetes do it { is_expected.to eq(YAML.load_file(path)) } end end + + describe '#add_terminal_auth' do + it 'adds authentication parameters to a hash' do + terminal = { original: 'value' } + + add_terminal_auth(terminal, token: 'foo', max_session_time: 0, ca_pem: 'bar') + + expect(terminal).to eq( + original: 'value', + headers: { 'Authorization' => ['Bearer foo'] }, + max_session_time: 0, + ca_pem: 'bar' + ) + end + end end diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb new file mode 100644 index 00000000000..ab305fb2316 --- /dev/null +++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe Gitlab::ManifestImport::Manifest, :postgresql do + let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) } + let(:manifest) { described_class.new(file) } + + describe '#valid?' do + context 'valid file' do + it { expect(manifest.valid?).to be true } + end + + context 'missing or invalid attributes' do + let(:file) { Tempfile.new('foo') } + + before do + content = <<~EOS + <manifest> + <remote review="invalid-url" /> + <project name="platform/build"/> + </manifest> + EOS + + file.write(content) + file.rewind + end + + it { expect(manifest.valid?).to be false } + + describe 'errors' do + before do + manifest.valid? + end + + it { expect(manifest.errors).to include('Make sure a <remote> tag is present and is valid.') } + it { expect(manifest.errors).to include('Make sure every <project> tag has name and path attributes.') } + end + end + end + + describe '#projects' do + it { expect(manifest.projects.size).to eq(660) } + it { expect(manifest.projects[0][:name]).to eq('platform/build') } + it { expect(manifest.projects[0][:path]).to eq('build/make') } + it { expect(manifest.projects[0][:url]).to eq('https://android-review.googlesource.com/platform/build') } + end +end diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb new file mode 100644 index 00000000000..1d01d437535 --- /dev/null +++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::ManifestImport::ProjectCreator, :postgresql do + let(:group) { create(:group) } + let(:user) { create(:user) } + let(:repository) do + { + path: 'device/common', + url: 'https://android-review.googlesource.com/device/common' + } + end + + before do + group.add_owner(user) + end + + subject { described_class.new(repository, group, user) } + + describe '#execute' do + it { expect(subject.execute).to be_a(Project) } + it { expect { subject.execute }.to change { Project.count }.by(1) } + it { expect { subject.execute }.to change { Group.count }.by(1) } + + it 'creates project with valid full path and import url' do + subject.execute + + project = Project.last + + expect(project.full_path).to eq(File.join(group.path, 'device/common')) + expect(project.import_url).to eq('https://android-review.googlesource.com/device/common') + end + end +end diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb index b24c9882c0c..7a3a9ab875b 100644 --- a/spec/lib/gitlab/middleware/go_spec.rb +++ b/spec/lib/gitlab/middleware/go_spec.rb @@ -79,7 +79,7 @@ describe Gitlab::Middleware::Go do let(:current_user) { project.creator } before do - project.team.add_master(current_user) + project.team.add_maintainer(current_user) end shared_examples 'authenticated' do diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb index 1dbead16d5b..c1b84e9f077 100644 --- a/spec/lib/gitlab/popen_spec.rb +++ b/spec/lib/gitlab/popen_spec.rb @@ -55,6 +55,19 @@ describe Gitlab::Popen do end end + context 'with a process that writes a lot of data to stderr' do + let(:test_string) { 'The quick brown fox jumped over the lazy dog' } + # The pipe buffer is typically 64K. This string is about 440K. + let(:spew_command) { ['bash', '-c', "for i in {1..10000}; do echo '#{test_string}' 1>&2; done"] } + + it 'returns zero' do + output, status = @klass.new.popen(spew_command, path) + + expect(output).to include(test_string) + expect(status).to eq(0) + end + end + context 'without a directory argument' do before do @output, @status = @klass.new.popen(%w(ls)) diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index f3cd6961e94..00c62c7bf96 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -41,7 +41,7 @@ describe Gitlab::ProjectAuthorizations do it 'includes the correct access levels' do mapping = map_access_levels(authorizations) - expect(mapping[owned_project.id]).to eq(Gitlab::Access::MASTER) + expect(mapping[owned_project.id]).to eq(Gitlab::Access::MAINTAINER) expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER) expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER) end @@ -62,11 +62,11 @@ describe Gitlab::ProjectAuthorizations do end it 'uses the greatest access level when a user is a member of a nested group' do - nested_group.add_master(user) + nested_group.add_maintainer(user) mapping = map_access_levels(authorizations) - expect(mapping[nested_project.id]).to eq(Gitlab::Access::MASTER) + expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER) end end end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 50224bde722..767a3092c73 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -385,7 +385,7 @@ describe Gitlab::ProjectSearchResults do let!(:private_project) { create(:project, :private, :repository, creator: creator, namespace: creator.namespace) } let(:team_master) do user = create(:user, username: 'private-project-master') - private_project.add_master(user) + private_project.add_maintainer(user) user end let(:team_reporter) do diff --git a/spec/lib/gitlab/slash_commands/issue_move_spec.rb b/spec/lib/gitlab/slash_commands/issue_move_spec.rb index d41441c9472..9a990e1fad7 100644 --- a/spec/lib/gitlab/slash_commands/issue_move_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_move_spec.rb @@ -27,7 +27,7 @@ describe Gitlab::SlashCommands::IssueMove, service: true do set(:other_project) { create(:project, namespace: project.namespace) } before do - [project, other_project].each { |prj| prj.add_master(user) } + [project, other_project].each { |prj| prj.add_maintainer(user) } end subject { described_class.new(project, chat_name) } diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb index 8e7df946529..724c76ade6e 100644 --- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::SlashCommands::IssueNew do let(:regex_match) { described_class.match("issue create bird is the word") } before do - project.add_master(user) + project.add_maintainer(user) end subject do diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb index 189e9592f1b..47787307990 100644 --- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb @@ -22,7 +22,7 @@ describe Gitlab::SlashCommands::IssueSearch do context 'the user has access' do before do - project.add_master(user) + project.add_maintainer(user) end it 'returns all results' do diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb index b1db1638237..5c4ba2736ba 100644 --- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb +++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::SlashCommands::IssueShow do let(:regex_match) { described_class.match("issue show #{issue.iid}") } before do - project.add_master(user) + project.add_maintainer(user) end subject do diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb index fc8991fd31f..758a9bc5a2b 100644 --- a/spec/lib/gitlab/url_sanitizer_spec.rb +++ b/spec/lib/gitlab/url_sanitizer_spec.rb @@ -92,6 +92,7 @@ describe Gitlab::UrlSanitizer do context 'credentials in URL' do where(:url, :credentials) do 'http://foo:bar@example.com' | { user: 'foo', password: 'bar' } + 'http://foo:bar:baz@example.com' | { user: 'foo', password: 'bar:baz' } 'http://:bar@example.com' | { user: nil, password: 'bar' } 'http://foo:@example.com' | { user: 'foo', password: nil } 'http://foo@example.com' | { user: 'foo', password: nil } diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 0469d984a40..9da06bb40f4 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -9,8 +9,8 @@ describe Gitlab::UserAccess do describe '#can_push_to_branch?' do describe 'push to none protected branch' do - it 'returns true if user is a master' do - project.add_master(user) + it 'returns true if user is a maintainer' do + project.add_maintainer(user) expect(access.can_push_to_branch?('random_branch')).to be_truthy end @@ -38,8 +38,8 @@ describe Gitlab::UserAccess do expect(access.can_push_to_branch?('master')).to be_truthy end - it 'returns true if user is master' do - empty_project.add_master(user) + it 'returns true if user is maintainer' do + empty_project.add_maintainer(user) expect(project_access.can_push_to_branch?('master')).to be_truthy end @@ -83,8 +83,8 @@ describe Gitlab::UserAccess do expect(access.can_push_to_branch?(branch.name)).to be_truthy end - it 'returns true if user is a master' do - project.add_master(user) + it 'returns true if user is a maintainer' do + project.add_maintainer(user) expect(access.can_push_to_branch?(branch.name)).to be_truthy end @@ -113,8 +113,8 @@ describe Gitlab::UserAccess do @branch = create :protected_branch, :developers_can_push, project: project end - it 'returns true if user is a master' do - project.add_master(user) + it 'returns true if user is a maintainer' do + project.add_maintainer(user) expect(access.can_push_to_branch?(@branch.name)).to be_truthy end @@ -170,8 +170,8 @@ describe Gitlab::UserAccess do @branch = create :protected_branch, :developers_can_merge, project: project end - it 'returns true if user is a master' do - project.add_master(user) + it 'returns true if user is a maintainer' do + project.add_maintainer(user) expect(access.can_merge_to_branch?(@branch.name)).to be_truthy end @@ -192,8 +192,8 @@ describe Gitlab::UserAccess do describe '#can_create_tag?' do describe 'push to none protected tag' do - it 'returns true if user is a master' do - project.add_user(user, :master) + it 'returns true if user is a maintainer' do + project.add_user(user, :maintainer) expect(access.can_create_tag?('random_tag')).to be_truthy end @@ -215,8 +215,8 @@ describe Gitlab::UserAccess do let(:tag) { create(:protected_tag, project: project, name: "test") } let(:not_existing_tag) { create :protected_tag, project: project } - it 'returns true if user is a master' do - project.add_user(user, :master) + it 'returns true if user is a maintainer' do + project.add_user(user, :maintainer) expect(access.can_create_tag?(tag.name)).to be_truthy end @@ -239,8 +239,8 @@ describe Gitlab::UserAccess do @tag = create(:protected_tag, :developers_can_create, project: project) end - it 'returns true if user is a master' do - project.add_user(user, :master) + it 'returns true if user is a maintainer' do + project.add_user(user, :maintainer) expect(access.can_create_tag?(@tag.name)).to be_truthy end @@ -261,8 +261,8 @@ describe Gitlab::UserAccess do describe '#can_delete_branch?' do describe 'delete unprotected branch' do - it 'returns true if user is a master' do - project.add_user(user, :master) + it 'returns true if user is a maintainer' do + project.add_user(user, :maintainer) expect(access.can_delete_branch?('random_branch')).to be_truthy end @@ -283,8 +283,8 @@ describe Gitlab::UserAccess do describe 'delete protected branch' do let(:branch) { create(:protected_branch, project: project, name: "test") } - it 'returns true if user is a master' do - project.add_user(user, :master) + it 'returns true if user is a maintainer' do + project.add_user(user, :maintainer) expect(access.can_delete_branch?(branch.name)).to be_truthy end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 98a1865d347..23869f3d2da 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -68,34 +68,22 @@ describe Gitlab::Workhorse do let(:diff_refs) { double(base_sha: "base", head_sha: "head") } subject { described_class.send_git_patch(repository, diff_refs) } - context 'when Gitaly workhorse_send_git_patch feature is enabled' do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) - - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-format-patch") - expect(params).to eq({ - 'GitalyServer' => { - address: Gitlab::GitalyClient.address(project.repository_storage), - token: Gitlab::GitalyClient.token(project.repository_storage) - }, - 'RawPatchRequest' => Gitaly::RawPatchRequest.new( - repository: repository.gitaly_repository, - left_commit_id: 'base', - right_commit_id: 'head' - ).to_json - }.deep_stringify_keys) - end - end - - context 'when Gitaly workhorse_send_git_patch feature is disabled', :disable_gitaly do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) - expect(key).to eq("Gitlab-Workhorse-Send-Data") - expect(command).to eq("git-format-patch") - expect(params).to eq("RepoPath" => repository.path_to_repo, "ShaFrom" => "base", "ShaTo" => "head") - end + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("git-format-patch") + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'RawPatchRequest' => Gitaly::RawPatchRequest.new( + repository: repository.gitaly_repository, + left_commit_id: 'base', + right_commit_id: 'head' + ).to_json + }.deep_stringify_keys) end end |