diff options
Diffstat (limited to 'spec/lib')
20 files changed, 527 insertions, 247 deletions
diff --git a/spec/lib/after_commit_queue_spec.rb b/spec/lib/after_commit_queue_spec.rb new file mode 100644 index 00000000000..6e7c2ec2363 --- /dev/null +++ b/spec/lib/after_commit_queue_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe AfterCommitQueue do + it 'runs after transaction is committed' do + called = false + test_proc = proc { called = true } + + project = build(:project) + project.run_after_commit(&test_proc) + + project.save + + expect(called).to be true + end +end diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb index fb3ef04b860..59deca7757b 100644 --- a/spec/lib/api/helpers/pagination_spec.rb +++ b/spec/lib/api/helpers/pagination_spec.rb @@ -52,7 +52,13 @@ describe API::Helpers::Pagination do expect_header('X-Page', '1') expect_header('X-Next-Page', '2') expect_header('X-Prev-Page', '') - expect_header('Link', any_args) + + expect_header('Link', anything) do |_key, val| + expect(val).to include('rel="first"') + expect(val).to include('rel="last"') + expect(val).to include('rel="next"') + expect(val).not_to include('rel="prev"') + end subject.paginate(resource) end @@ -75,15 +81,53 @@ describe API::Helpers::Pagination do expect_header('X-Page', '2') expect_header('X-Next-Page', '') expect_header('X-Prev-Page', '1') - expect_header('Link', any_args) + + expect_header('Link', anything) do |_key, val| + expect(val).to include('rel="first"') + expect(val).to include('rel="last"') + expect(val).to include('rel="prev"') + expect(val).not_to include('rel="next"') + end + + subject.paginate(resource) + end + end + end + + context 'when resource empty' do + describe 'first page' do + before do + allow(subject).to receive(:params) + .and_return({ page: 1, per_page: 2 }) + end + + it 'returns appropriate amount of resources' do + expect(subject.paginate(resource).count).to eq 0 + end + + it 'adds appropriate headers' do + expect_header('X-Total', '0') + expect_header('X-Total-Pages', '1') + expect_header('X-Per-Page', '2') + expect_header('X-Page', '1') + expect_header('X-Next-Page', '') + expect_header('X-Prev-Page', '') + + expect_header('Link', anything) do |_key, val| + expect(val).to include('rel="first"') + expect(val).to include('rel="last"') + expect(val).not_to include('rel="prev"') + expect(val).not_to include('rel="next"') + expect(val).not_to include('page=0') + end subject.paginate(resource) end end end - def expect_header(name, value) - expect(subject).to receive(:header).with(name, value) + def expect_header(*args, &block) + expect(subject).to receive(:header).with(*args, &block) end def expect_message(method) diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index 7cd2ce82eda..c0427639746 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -134,6 +134,17 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits do include_examples 'updated MR diff' end + context 'when the merge request diffs do not have a_mode and b_mode set' do + let(:commits) { merge_request_diff.commits.map(&:to_hash) } + let(:expected_diffs) { diffs_to_hashes(merge_request_diff.merge_request_diff_files) } + + let(:diffs) do + expected_diffs.map { |diff| diff.except(:a_mode, :b_mode) } + end + + include_examples 'updated MR diff' + end + context 'when the merge request diffs have binary content' do let(:commits) { merge_request_diff.commits.map(&:to_hash) } let(:expected_diffs) { diffs } diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb index 87f45619e7a..0d5fffa38ff 100644 --- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb +++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb @@ -210,7 +210,11 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event do end end -describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads do +## +# The background migration relies on a temporary table, hence we're migrating +# to a specific version of the database where said table is still present. +# +describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170608152748 do let(:migration) { described_class.new } let(:project) { create(:project_empty_repo) } let(:author) { create(:user) } @@ -229,21 +233,6 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads do ) end - # The background migration relies on a temporary table, hence we're migrating - # to a specific version of the database where said table is still present. - before :all do - ActiveRecord::Migration.verbose = false - - ActiveRecord::Migrator - .migrate(ActiveRecord::Migrator.migrations_paths, 20170608152748) - end - - after :all do - ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths) - - ActiveRecord::Migration.verbose = true - end - describe '#perform' do it 'returns if data should not be migrated' do allow(migration).to receive(:migrate?).and_return(false) diff --git a/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb new file mode 100644 index 00000000000..878158910be --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20170711145320 do + let(:projects) { table(:projects) } + let(:pipelines) { table(:ci_pipelines) } + let(:stages) { table(:ci_stages) } + let(:jobs) { table(:ci_builds) } + + STATUSES = { created: 0, pending: 1, running: 2, success: 3, + failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze + + before do + projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1') + pipelines.create!(id: 1, project_id: 1, ref: 'master', sha: 'adf43c3a') + stages.create!(id: 1, pipeline_id: 1, project_id: 1, name: 'test', status: nil) + stages.create!(id: 2, pipeline_id: 1, project_id: 1, name: 'deploy', status: nil) + end + + context 'when stage status is known' do + before do + create_job(project: 1, pipeline: 1, stage: 'test', status: 'success') + create_job(project: 1, pipeline: 1, stage: 'test', status: 'running') + create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'failed') + end + + it 'sets a correct stage status' do + described_class.new.perform(1, 2) + + expect(stages.first.status).to eq STATUSES[:running] + expect(stages.second.status).to eq STATUSES[:failed] + end + end + + context 'when stage status is not known' do + it 'sets a skipped stage status' do + described_class.new.perform(1, 2) + + expect(stages.first.status).to eq STATUSES[:skipped] + expect(stages.second.status).to eq STATUSES[:skipped] + end + end + + context 'when stage status includes status of a retried job' do + before do + create_job(project: 1, pipeline: 1, stage: 'test', status: 'canceled') + create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'failed', retried: true) + create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'success') + end + + it 'sets a correct stage status' do + described_class.new.perform(1, 2) + + expect(stages.first.status).to eq STATUSES[:canceled] + expect(stages.second.status).to eq STATUSES[:success] + end + end + + context 'when some job in the stage is blocked / manual' do + before do + create_job(project: 1, pipeline: 1, stage: 'test', status: 'failed') + create_job(project: 1, pipeline: 1, stage: 'test', status: 'manual') + create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'success', when: 'manual') + end + + it 'sets a correct stage status' do + described_class.new.perform(1, 2) + + expect(stages.first.status).to eq STATUSES[:manual] + expect(stages.second.status).to eq STATUSES[:success] + end + end + + def create_job(project:, pipeline:, stage:, status:, **opts) + stages = { test: 1, build: 2, deploy: 3 } + + jobs.create!(project_id: project, commit_id: pipeline, + stage_idx: stages[stage.to_sym], stage: stage, + status: status, **opts) + end +end diff --git a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb index 046b096e366..7e17437fa2a 100644 --- a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb +++ b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb @@ -6,9 +6,9 @@ describe Gitlab::Diff::InlineDiffMarkdownMarker do let(:inline_diffs) { [2..5] } let(:subject) { described_class.new(raw).mark(inline_diffs, mode: :deletion) } - it 'marks the range' do - expect(subject).to eq("ab{-c 'd-}ef'") - expect(subject).to be_html_safe + it 'does not escape html etities and marks the range' do + expect(subject).to eq("ab{-c 'd-}ef'") + expect(subject).not_to be_html_safe end end end diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb index c3bf34c24ae..7296bbf5df3 100644 --- a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb +++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb @@ -2,11 +2,13 @@ require 'spec_helper' describe Gitlab::Diff::InlineDiffMarker do describe '#mark' do + let(:inline_diffs) { [2..5] } + let(:raw) { "abc 'def'" } + + subject { described_class.new(raw, rich).mark(inline_diffs) } + context "when the rich text is html safe" do - let(:raw) { "abc 'def'" } let(:rich) { %{<span class="abc">abc</span><span class="space"> </span><span class="def">'def'</span>}.html_safe } - let(:inline_diffs) { [2..5] } - let(:subject) { described_class.new(raw, rich).mark(inline_diffs) } it 'marks the range' do expect(subject).to eq(%{<span class="abc">ab<span class="idiff left">c</span></span><span class="space"><span class="idiff"> </span></span><span class="def"><span class="idiff right">'d</span>ef'</span>}) @@ -15,12 +17,10 @@ describe Gitlab::Diff::InlineDiffMarker do end context "when the text text is not html safe" do - let(:raw) { "abc 'def'" } - let(:inline_diffs) { [2..5] } - let(:subject) { described_class.new(raw).mark(inline_diffs) } + let(:rich) { "abc 'def' differs" } it 'marks the range' do - expect(subject).to eq(%{ab<span class="idiff left right">c 'd</span>ef'}) + expect(subject).to eq(%{ab<span class="idiff left right">c 'd</span>ef' differs}) expect(subject).to be_html_safe end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 4ef5d9070a2..8ec8dfe6acf 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -235,18 +235,10 @@ describe Gitlab::Git::Repository, seed_helper: true do it { is_expected.to be < 2 } end - describe '#has_commits?' do - it { expect(repository.has_commits?).to be_truthy } - end - describe '#empty?' do it { expect(repository.empty?).to be_falsey } end - describe '#bare?' do - it { expect(repository.bare?).to be_truthy } - end - describe '#ref_names' do let(:ref_names) { repository.ref_names } subject { ref_names } @@ -441,15 +433,6 @@ describe Gitlab::Git::Repository, seed_helper: true do end end - describe "#remote_names" do - let(:remotes) { repository.remote_names } - - it "should have one entry: 'origin'" do - expect(remotes.size).to eq(1) - expect(remotes.first).to eq("origin") - end - end - describe "#refs_hash" do let(:refs) { repository.refs_hash } @@ -1098,28 +1081,48 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#tag_exists?' do - it 'returns true for an existing tag' do - tag = repository.tag_names.first + shared_examples 'checks the existence of tags' do + it 'returns true for an existing tag' do + tag = repository.tag_names.first + + expect(repository.tag_exists?(tag)).to eq(true) + end - expect(repository.tag_exists?(tag)).to eq(true) + it 'returns false for a non-existing tag' do + expect(repository.tag_exists?('v9000')).to eq(false) + end end - it 'returns false for a non-existing tag' do - expect(repository.tag_exists?('v9000')).to eq(false) + context 'when Gitaly ref_exists_tags feature is enabled' do + it_behaves_like 'checks the existence of tags' + end + + context 'when Gitaly ref_exists_tags feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'checks the existence of tags' end end describe '#branch_exists?' do - it 'returns true for an existing branch' do - expect(repository.branch_exists?('master')).to eq(true) + shared_examples 'checks the existence of branches' do + it 'returns true for an existing branch' do + expect(repository.branch_exists?('master')).to eq(true) + end + + it 'returns false for a non-existing branch' do + expect(repository.branch_exists?('kittens')).to eq(false) + end + + it 'returns false when using an invalid branch name' do + expect(repository.branch_exists?('.bla')).to eq(false) + end end - it 'returns false for a non-existing branch' do - expect(repository.branch_exists?('kittens')).to eq(false) + context 'when Gitaly ref_exists_branches feature is enabled' do + it_behaves_like 'checks the existence of branches' end - it 'returns false when using an invalid branch name' do - expect(repository.branch_exists?('.bla')).to eq(false) + context 'when Gitaly ref_exists_branches feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'checks the existence of branches' end end diff --git a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb b/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb index 12366151f44..c708b15853a 100644 --- a/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb +++ b/spec/lib/gitlab/git/storage/forked_storage_check_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Git::Storage::ForkedStorageCheck, skip_database_cleaner: true do +describe Gitlab::Git::Storage::ForkedStorageCheck, broken_storage: true, skip_database_cleaner: true do let(:existing_path) do existing_path = TestEnv.repos_path FileUtils.mkdir_p(existing_path) diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index 2d6ea37d0ac..80dc49e99cb 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -1,21 +1,17 @@ require 'spec_helper' describe Gitlab::GitAccess do - let(:pull_access_check) { access.check('git-upload-pack', '_any') } - let(:push_access_check) { access.check('git-receive-pack', '_any') } - let(:access) { described_class.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) } - let(:project) { create(:project, :repository) } - let(:user) { create(:user) } + set(:user) { create(:user) } + let(:actor) { user } + let(:project) { create(:project, :repository) } let(:protocol) { 'ssh' } + let(:authentication_abilities) { %i[read_project download_code push_code] } let(:redirected_path) { nil } - let(:authentication_abilities) do - [ - :read_project, - :download_code, - :push_code - ] - end + + let(:access) { described_class.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) } + let(:push_access_check) { access.check('git-receive-pack', '_any') } + let(:pull_access_check) { access.check('git-upload-pack', '_any') } describe '#check with single protocols allowed' do def disable_protocol(protocol) @@ -27,12 +23,11 @@ describe Gitlab::GitAccess do disable_protocol('ssh') end - it 'blocks ssh git push' do - expect { push_access_check }.to raise_unauthorized('Git access over SSH is not allowed') - end - - it 'blocks ssh git pull' do - expect { pull_access_check }.to raise_unauthorized('Git access over SSH is not allowed') + it 'blocks ssh git push and pull' do + aggregate_failures do + expect { push_access_check }.to raise_unauthorized('Git access over SSH is not allowed') + expect { pull_access_check }.to raise_unauthorized('Git access over SSH is not allowed') + end end end @@ -43,12 +38,11 @@ describe Gitlab::GitAccess do disable_protocol('http') end - it 'blocks http push' do - expect { push_access_check }.to raise_unauthorized('Git access over HTTP is not allowed') - end - - it 'blocks http git pull' do - expect { pull_access_check }.to raise_unauthorized('Git access over HTTP is not allowed') + it 'blocks http push and pull' do + aggregate_failures do + expect { push_access_check }.to raise_unauthorized('Git access over HTTP is not allowed') + expect { pull_access_check }.to raise_unauthorized('Git access over HTTP is not allowed') + end end end end @@ -65,22 +59,20 @@ describe Gitlab::GitAccess do deploy_key.projects << project end - it 'allows pull access' do - expect { pull_access_check }.not_to raise_error - end - - it 'allows push access' do - expect { push_access_check }.not_to raise_error + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end end end context 'when the Deploykey does not have access to the project' do - it 'blocks pulls with "not found"' do - expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') - end - - it 'blocks pushes with "not found"' do - expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') + it 'blocks push and pull with "not found"' do + aggregate_failures do + expect { push_access_check }.to raise_not_found + expect { pull_access_check }.to raise_not_found + end end end end @@ -88,25 +80,23 @@ describe Gitlab::GitAccess do context 'when actor is a User' do context 'when the User can read the project' do before do - project.team << [user, :master] + project.add_master(user) end - it 'allows pull access' do - expect { pull_access_check }.not_to raise_error - end - - it 'allows push access' do - expect { push_access_check }.not_to raise_error + it 'allows push and pull access' do + aggregate_failures do + expect { pull_access_check }.not_to raise_error + expect { push_access_check }.not_to raise_error + end end end context 'when the User cannot read the project' do - it 'blocks pulls with "not found"' do - expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') - end - - it 'blocks pushes with "not found"' do - expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') + it 'blocks push and pull with "not found"' do + aggregate_failures do + expect { push_access_check }.to raise_not_found + expect { pull_access_check }.to raise_not_found + end end end end @@ -121,7 +111,7 @@ describe Gitlab::GitAccess do end it 'does not block pushes with "not found"' do - expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') + expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) end end end @@ -137,17 +127,17 @@ describe Gitlab::GitAccess do end it 'does not block pushes with "not found"' do - expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') + expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) end end context 'when guests cannot read the project' do it 'blocks pulls with "not found"' do - expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') + expect { pull_access_check }.to raise_not_found end it 'blocks pushes with "not found"' do - expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') + expect { push_access_check }.to raise_not_found end end end @@ -156,48 +146,50 @@ describe Gitlab::GitAccess do context 'when the project is nil' do let(:project) { nil } - it 'blocks any command with "not found"' do - expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') - expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') + it 'blocks push and pull with "not found"' do + aggregate_failures do + expect { pull_access_check }.to raise_not_found + expect { push_access_check }.to raise_not_found + end end end end describe '#check_project_moved!' do before do - project.team << [user, :master] + project.add_master(user) end context 'when a redirect was not followed to find the project' do - context 'pull code' do - it { expect { pull_access_check }.not_to raise_error } - end - - context 'push code' do - it { expect { push_access_check }.not_to raise_error } + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end end end context 'when a redirect was followed to find the project' do let(:redirected_path) { 'some/other-path' } - context 'pull code' do - it { expect { pull_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) } - it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) } + it 'blocks push and pull access' do + aggregate_failures do + expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/) + expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/) - context 'http protocol' do - let(:protocol) { 'http' } - it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) } + expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /Project '#{redirected_path}' was moved to '#{project.full_path}'/) + expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/) end end - context 'push code' do - it { expect { push_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) } - it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) } + context 'http protocol' do + let(:protocol) { 'http' } - context 'http protocol' do - let(:protocol) { 'http' } - it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) } + it 'includes the path to the project using HTTP' do + aggregate_failures do + expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/) + expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/) + end end end end @@ -242,40 +234,28 @@ describe Gitlab::GitAccess do end describe '#check_download_access!' do - describe 'master permissions' do - before do - project.team << [user, :master] - end + it 'allows masters to pull' do + project.add_master(user) - context 'pull code' do - it { expect { pull_access_check }.not_to raise_error } - end + expect { pull_access_check }.not_to raise_error end - describe 'guest permissions' do - before do - project.team << [user, :guest] - end + it 'disallows guests to pull' do + project.add_guest(user) - context 'pull code' do - it { expect { pull_access_check }.to raise_unauthorized('You are not allowed to download code from this project.') } - end + expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download]) end - describe 'blocked user' do - before do - project.team << [user, :master] - user.block - end + it 'disallows blocked users to pull' do + project.add_master(user) + user.block - context 'pull code' do - it { expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.') } - end + expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.') end describe 'without access to project' do context 'pull code' do - it { expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { pull_access_check }.to raise_not_found } end context 'when project is public' do @@ -292,7 +272,7 @@ describe Gitlab::GitAccess do it 'does not give access to download code' do public_project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED) - expect { pull_access_check }.to raise_unauthorized('You are not allowed to download code from this project.') + expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download]) end end end @@ -321,13 +301,13 @@ describe Gitlab::GitAccess do context 'from internal project' do let(:project) { create(:project, :internal, :repository) } - it { expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { pull_access_check }.to raise_not_found } end context 'from private project' do let(:project) { create(:project, :private, :repository) } - it { expect { pull_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { pull_access_check }.to raise_not_found } end end end @@ -369,7 +349,7 @@ describe Gitlab::GitAccess do context 'when is not member of the project' do context 'pull code' do - it { expect { pull_access_check }.to raise_unauthorized('You are not allowed to download code from this project.') } + it { expect { pull_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:download]) } end end end @@ -428,28 +408,30 @@ describe Gitlab::GitAccess do end end - # Run permission checks for a user def self.run_permission_checks(permissions_matrix) - permissions_matrix.keys.each do |role| - describe "#{role} access" do - before do - if role == :admin - user.update_attribute(:admin, true) - else - project.team << [user, role] - end + permissions_matrix.each_pair do |role, matrix| + # Run through the entire matrix for this role in one test to avoid + # repeated setup. + # + # Expectations are given a custom failure message proc so that it's + # easier to identify which check(s) failed. + it "has the correct permissions for #{role}s" do + if role == :admin + user.update_attribute(:admin, true) + else + project.team << [user, role] end - permissions_matrix[role].each do |action, allowed| - context action.to_s do - subject { access.send(:check_push_access!, changes[action]) } + aggregate_failures do + matrix.each do |action, allowed| + check = -> { access.send(:check_push_access!, changes[action]) } - it do - if allowed - expect { subject }.not_to raise_error - else - expect { subject }.to raise_error(Gitlab::GitAccess::UnauthorizedError) - end + if allowed + expect(&check).not_to raise_error, + -> { "expected #{action} to be allowed" } + else + expect(&check).to raise_error(Gitlab::GitAccess::UnauthorizedError), + -> { "expected #{action} to be disallowed" } end end end @@ -588,26 +570,26 @@ describe Gitlab::GitAccess do project.team << [user, :reporter] end - it { expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) } end context 'when unauthorized' do context 'to public project' do let(:project) { create(:project, :public, :repository) } - it { expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) } end context 'to internal project' do let(:project) { create(:project, :internal, :repository) } - it { expect { push_access_check }.to raise_unauthorized('You are not allowed to upload code for this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:upload]) } end context 'to private project' do let(:project) { create(:project, :private, :repository) } - it { expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { push_access_check }.to raise_not_found } end end end @@ -631,19 +613,19 @@ describe Gitlab::GitAccess do context 'to public project' do let(:project) { create(:project, :public, :repository) } - it { expect { push_access_check }.to raise_unauthorized('This deploy key does not have write access to this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) } end context 'to internal project' do let(:project) { create(:project, :internal, :repository) } - it { expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { push_access_check }.to raise_not_found } end context 'to private project' do let(:project) { create(:project, :private, :repository) } - it { expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { push_access_check }.to raise_not_found } end end end @@ -656,26 +638,26 @@ describe Gitlab::GitAccess do key.projects << project end - it { expect { push_access_check }.to raise_unauthorized('This deploy key does not have write access to this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) } end context 'when unauthorized' do context 'to public project' do let(:project) { create(:project, :public, :repository) } - it { expect { push_access_check }.to raise_unauthorized('This deploy key does not have write access to this project.') } + it { expect { push_access_check }.to raise_unauthorized(described_class::ERROR_MESSAGES[:deploy_key_upload]) } end context 'to internal project' do let(:project) { create(:project, :internal, :repository) } - it { expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { push_access_check }.to raise_not_found } end context 'to private project' do let(:project) { create(:project, :private, :repository) } - it { expect { push_access_check }.to raise_not_found('The project you were looking for could not be found.') } + it { expect { push_access_check }.to raise_not_found } end end end @@ -687,8 +669,9 @@ describe Gitlab::GitAccess do raise_error(Gitlab::GitAccess::UnauthorizedError, message) end - def raise_not_found(message) - raise_error(Gitlab::GitAccess::NotFoundError, message) + def raise_not_found + raise_error(Gitlab::GitAccess::NotFoundError, + Gitlab::GitAccess::ERROR_MESSAGES[:project_not_found]) end def build_authentication_abilities diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index 7fe698fcb18..2eaf4222964 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -111,6 +111,20 @@ describe Gitlab::GitalyClient::CommitService do client.tree_entries(repository, revision, path) end + + context 'with UTF-8 params strings' do + let(:revision) { "branch\u011F" } + let(:path) { "foo/\u011F.txt" } + + it 'handles string encodings correctly' do + expect_any_instance_of(Gitaly::CommitService::Stub) + .to receive(:get_tree_entries) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return([]) + + client.tree_entries(repository, revision, path) + end + end end describe '#find_commit' do diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb index 46efc1b18f0..6f59750b4da 100644 --- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' describe Gitlab::GitalyClient::RefService do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:storage_name) { project.repository_storage } let(:relative_path) { project.disk_path + '.git' } - let(:client) { described_class.new(project.repository) } + let(:repository) { project.repository } + let(:client) { described_class.new(repository) } describe '#branches' do it 'sends a find_all_branches message' do @@ -84,11 +85,23 @@ describe Gitlab::GitalyClient::RefService do end describe '#find_ref_name', seed_helper: true do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } - let(:client) { described_class.new(repository) } subject { client.find_ref_name(SeedRepo::Commit::ID, 'refs/heads/master') } it { is_expected.to be_utf8 } it { is_expected.to eq('refs/heads/master') } end + + describe '#ref_exists?', seed_helper: true do + it 'finds the master branch ref' do + expect(client.ref_exists?('refs/heads/master')).to eq(true) + end + + it 'returns false for an illegal tag name ref' do + expect(client.ref_exists?('refs/tags/.this-tag-name-is-illegal')).to eq(false) + end + + it 'raises an argument error if the ref name parameter does not start with refs/' do + expect { client.ref_exists?('reXXXXX') }.to raise_error(ArgumentError) + end + end end diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb new file mode 100644 index 00000000000..fd5f984601e --- /dev/null +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe Gitlab::GitalyClient::RepositoryService do + let(:project) { create(:project) } + let(:storage_name) { project.repository_storage } + let(:relative_path) { project.disk_path + '.git' } + let(:client) { described_class.new(project.repository) } + + describe '#exists?' do + it 'sends a repository_exists message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:repository_exists) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(exists: true)) + + client.exists? + end + end + + describe '#garbage_collect' do + it 'sends a garbage_collect message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:garbage_collect) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(:garbage_collect_response)) + + client.garbage_collect(true) + end + end + + describe '#repack_full' do + it 'sends a repack_full message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:repack_full) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(:repack_full_response)) + + client.repack_full(true) + end + end + + describe '#repack_incremental' do + it 'sends a repack_incremental message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:repack_incremental) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(:repack_incremental_response)) + + client.repack_incremental + end + end + + describe '#repository_size' do + it 'sends a repository_size message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:repository_size) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(size: 0) + + client.repository_size + end + end + + describe '#apply_gitattributes' do + let(:revision) { 'master' } + + it 'sends an apply_gitattributes message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:apply_gitattributes) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(double(:apply_gitattributes_response)) + + client.apply_gitattributes(revision) + end + end +end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 4e631e13410..331b7cf2fea 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -2522,7 +2522,7 @@ "id": 27, "target_branch": "feature", "source_branch": "feature_conflict", - "source_project_id": 5, + "source_project_id": 999, "author_id": 1, "assignee_id": null, "title": "MR1", @@ -2536,6 +2536,9 @@ "position": 0, "updated_by_id": null, "merge_error": null, + "diff_head_sha": "HEAD", + "source_branch_sha": "ABCD", + "target_branch_sha": "DCBA", "merge_params": { "force_remove_source_branch": null }, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 956f1d56eb4..c10427d798f 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -10,6 +10,13 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do @shared = Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') @project = create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') + + allow(@project.repository).to receive(:fetch_ref).and_return(true) + allow(@project.repository.raw).to receive(:rugged_branch_exists?).and_return(false) + + expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA') + allow_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch) + project_tree_restorer = described_class.new(user: @user, shared: @shared, project: @project) @restored_project_json = project_tree_restorer.restore 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 a278f89c1a1..065b0ec6658 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -11,6 +11,8 @@ describe Gitlab::ImportExport::ProjectTreeSaver do before do project.team << [user, :master] 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') end after do @@ -43,6 +45,14 @@ describe Gitlab::ImportExport::ProjectTreeSaver do expect(saved_project_json['merge_requests'].first['milestone']).not_to be_empty end + it 'has merge request\'s source branch SHA' do + expect(saved_project_json['merge_requests'].first['source_branch_sha']).to eq('ABCD') + end + + it 'has merge request\'s target branch SHA' do + expect(saved_project_json['merge_requests'].first['target_branch_sha']).to eq('DCBA') + end + it 'has events' do expect(saved_project_json['merge_requests'].first['milestone']['events']).not_to be_empty end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index ae3b0173160..a5e03e149a7 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -227,6 +227,8 @@ Ci::Pipeline: Ci::Stage: - id - name +- status +- lock_version - project_id - pipeline_id - created_at diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb index 6186cec2689..b0b4fdc09bc 100644 --- a/spec/lib/gitlab/job_waiter_spec.rb +++ b/spec/lib/gitlab/job_waiter_spec.rb @@ -1,30 +1,39 @@ require 'spec_helper' describe Gitlab::JobWaiter do - describe '#wait' do - let(:waiter) { described_class.new(%w(a)) } - it 'returns when all jobs have been completed' do - expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a)) - .and_return(true) + describe '.notify' do + it 'pushes the jid to the named queue' do + key = 'gitlab:job_waiter:foo' + jid = 1 - expect(waiter).not_to receive(:sleep) + redis = double('redis') + expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis) + expect(redis).to receive(:lpush).with(key, jid) - waiter.wait + described_class.notify(key, jid) end + end + + describe '#wait' do + let(:waiter) { described_class.new(2) } - it 'sleeps between checking the job statuses' do - expect(Gitlab::SidekiqStatus).to receive(:all_completed?) - .with(%w(a)) - .and_return(false, true) + it 'returns when all jobs have been completed' do + described_class.notify(waiter.key, 'a') + described_class.notify(waiter.key, 'b') - expect(waiter).to receive(:sleep).with(described_class::INTERVAL) + result = nil + expect { Timeout.timeout(1) { result = waiter.wait(2) } }.not_to raise_error - waiter.wait + expect(result).to contain_exactly('a', 'b') end - it 'returns when timing out' do - expect(waiter).not_to receive(:sleep) - waiter.wait(0) + it 'times out if not all jobs complete' do + described_class.notify(waiter.key, 'a') + + result = nil + expect { Timeout.timeout(2) { result = waiter.wait(1) } }.not_to raise_error + + expect(result).to contain_exactly('a') end end end diff --git a/spec/lib/gitlab/sidekiq_throttler_spec.rb b/spec/lib/gitlab/sidekiq_throttler_spec.rb index 6374ac80207..2dbb7bb7c34 100644 --- a/spec/lib/gitlab/sidekiq_throttler_spec.rb +++ b/spec/lib/gitlab/sidekiq_throttler_spec.rb @@ -1,28 +1,44 @@ require 'spec_helper' describe Gitlab::SidekiqThrottler do - before do - Sidekiq.options[:concurrency] = 35 - - stub_application_setting( - sidekiq_throttling_enabled: true, - sidekiq_throttling_factor: 0.1, - sidekiq_throttling_queues: %w[build project_cache] - ) - end - describe '#execute!' do - it 'sets limits on the selected queues' do - described_class.execute! + context 'when job throttling is enabled' do + before do + Sidekiq.options[:concurrency] = 35 + + stub_application_setting( + sidekiq_throttling_enabled: true, + sidekiq_throttling_factor: 0.1, + sidekiq_throttling_queues: %w[build project_cache] + ) + end + + it 'requires sidekiq-limit_fetch' do + expect(described_class).to receive(:require).with('sidekiq-limit_fetch').and_call_original + + described_class.execute! + end + + it 'sets limits on the selected queues' do + described_class.execute! + + expect(Sidekiq::Queue['build'].limit).to eq 4 + expect(Sidekiq::Queue['project_cache'].limit).to eq 4 + end + + it 'does not set limits on other queues' do + described_class.execute! - expect(Sidekiq::Queue['build'].limit).to eq 4 - expect(Sidekiq::Queue['project_cache'].limit).to eq 4 + expect(Sidekiq::Queue['merge'].limit).to be_nil + end end - it 'does not set limits on other queues' do - described_class.execute! + context 'when job throttling is disabled' do + it 'does not require sidekiq-limit_fetch' do + expect(described_class).not_to receive(:require).with('sidekiq-limit_fetch') - expect(Sidekiq::Queue['merge'].limit).to be_nil + described_class.execute! + end end end end diff --git a/spec/lib/gitlab/string_range_marker_spec.rb b/spec/lib/gitlab/string_range_marker_spec.rb index abeaa7f0ddb..6bc02459dbd 100644 --- a/spec/lib/gitlab/string_range_marker_spec.rb +++ b/spec/lib/gitlab/string_range_marker_spec.rb @@ -2,34 +2,39 @@ require 'spec_helper' describe Gitlab::StringRangeMarker do describe '#mark' do + def mark_diff(rich = nil) + raw = 'abc <def>' + inline_diffs = [2..5] + + described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:| + "LEFT#{text}RIGHT" + end + end + context "when the rich text is html safe" do - let(:raw) { "abc <def>" } let(:rich) { %{<span class="abc">abc</span><span class="space"> </span><span class="def"><def></span>}.html_safe } - let(:inline_diffs) { [2..5] } - subject do - described_class.new(raw, rich).mark(inline_diffs) do |text, left:, right:| - "LEFT#{text}RIGHT" - end - end it 'marks the inline diffs' do - expect(subject).to eq(%{<span class="abc">abLEFTcRIGHT</span><span class="space">LEFT RIGHT</span><span class="def">LEFT<dRIGHTef></span>}) - expect(subject).to be_html_safe + expect(mark_diff(rich)).to eq(%{<span class="abc">abLEFTcRIGHT</span><span class="space">LEFT RIGHT</span><span class="def">LEFT<dRIGHTef></span>}) + expect(mark_diff(rich)).to be_html_safe end end context "when the rich text is not html safe" do - let(:raw) { "abc <def>" } - let(:inline_diffs) { [2..5] } - subject do - described_class.new(raw).mark(inline_diffs) do |text, left:, right:| - "LEFT#{text}RIGHT" + context 'when rich text equals raw text' do + it 'marks the inline diffs' do + expect(mark_diff).to eq(%{abLEFTc <dRIGHTef>}) + expect(mark_diff).not_to be_html_safe end end - it 'marks the inline diffs' do - expect(subject).to eq(%{abLEFTc <dRIGHTef>}) - expect(subject).to be_html_safe + context 'when rich text doeas not equal raw text' do + let(:rich) { "abc <def> differs" } + + it 'marks the inline diffs' do + expect(mark_diff(rich)).to eq(%{abLEFTc <dRIGHTef> differs}) + expect(mark_diff(rich)).to be_html_safe + end end end end |