399056ed78
This saves about 128 MB of baseline RAM usage per Unicorn and
Sidekiq process (!).
Linguist wasn't detecting languages anymore from CE/EE since
9ae8b57467
. However, Linguist::BlobHelper
was still being depended on by BlobLike and others.
This removes the Linguist gem, given it isn't required anymore.
EscapeUtils were pulled in as dependency, but given Banzai depends on
it, it is now added explicitly.
Previously, Linguist was used to detect the best ACE mode. Instead,
we rely on ACE to guess the best mode based on the file extension.
306 lines
10 KiB
Ruby
306 lines
10 KiB
Ruby
require 'spec_helper'
|
|
|
|
describe Projects::MergeRequests::ConflictsController do
|
|
let(:project) { create(:project, :repository) }
|
|
let(:user) { project.owner }
|
|
let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
|
|
let(:merge_request_with_conflicts) do
|
|
create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project, merge_status: :unchecked) do |mr|
|
|
mr.mark_as_unmergeable
|
|
end
|
|
end
|
|
|
|
before do
|
|
sign_in(user)
|
|
end
|
|
|
|
describe 'GET show' do
|
|
context 'when the conflicts cannot be resolved in the UI' do
|
|
before do
|
|
allow(Gitlab::Git::Conflict::Parser).to receive(:parse)
|
|
.and_raise(Gitlab::Git::Conflict::Parser::UnmergeableFile)
|
|
|
|
get :show,
|
|
namespace_id: merge_request_with_conflicts.project.namespace.to_param,
|
|
project_id: merge_request_with_conflicts.project,
|
|
id: merge_request_with_conflicts.iid,
|
|
format: 'json'
|
|
end
|
|
|
|
it 'returns a 200 status code' do
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
end
|
|
|
|
it 'returns JSON with a message' do
|
|
expect(json_response.keys).to contain_exactly('message', 'type')
|
|
end
|
|
end
|
|
|
|
context 'with valid conflicts' do
|
|
before do
|
|
get :show,
|
|
namespace_id: merge_request_with_conflicts.project.namespace.to_param,
|
|
project_id: merge_request_with_conflicts.project,
|
|
id: merge_request_with_conflicts.iid,
|
|
format: 'json'
|
|
end
|
|
|
|
it 'matches the schema' do
|
|
expect(response).to match_response_schema('conflicts')
|
|
end
|
|
|
|
it 'includes meta info about the MR' do
|
|
expect(json_response['commit_message']).to include('Merge branch')
|
|
expect(json_response['commit_sha']).to match(/\h{40}/)
|
|
expect(json_response['source_branch']).to eq(merge_request_with_conflicts.source_branch)
|
|
expect(json_response['target_branch']).to eq(merge_request_with_conflicts.target_branch)
|
|
end
|
|
|
|
it 'includes each file that has conflicts' do
|
|
filenames = json_response['files'].map { |file| file['new_path'] }
|
|
|
|
expect(filenames).to contain_exactly('files/ruby/popen.rb', 'files/ruby/regex.rb')
|
|
end
|
|
|
|
it 'splits files into sections with lines' do
|
|
json_response['files'].each do |file|
|
|
file['sections'].each do |section|
|
|
expect(section).to include('conflict', 'lines')
|
|
|
|
section['lines'].each do |line|
|
|
if section['conflict']
|
|
expect(line['type']).to be_in(%w(old new))
|
|
expect(line.values_at('old_line', 'new_line')).to contain_exactly(nil, a_kind_of(Integer))
|
|
else
|
|
if line['type'].nil?
|
|
expect(line['old_line']).not_to eq(nil)
|
|
expect(line['new_line']).not_to eq(nil)
|
|
else
|
|
expect(line['type']).to eq('match')
|
|
expect(line['old_line']).to eq(nil)
|
|
expect(line['new_line']).to eq(nil)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'has unique section IDs across files' do
|
|
section_ids = json_response['files'].flat_map do |file|
|
|
file['sections'].map { |section| section['id'] }.compact
|
|
end
|
|
|
|
expect(section_ids.uniq).to eq(section_ids)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET conflict_for_path' do
|
|
def conflict_for_path(path)
|
|
get :conflict_for_path,
|
|
namespace_id: merge_request_with_conflicts.project.namespace.to_param,
|
|
project_id: merge_request_with_conflicts.project,
|
|
id: merge_request_with_conflicts.iid,
|
|
old_path: path,
|
|
new_path: path,
|
|
format: 'json'
|
|
end
|
|
|
|
context 'when the conflicts cannot be resolved in the UI' do
|
|
before do
|
|
allow(Gitlab::Git::Conflict::Parser).to receive(:parse)
|
|
.and_raise(Gitlab::Git::Conflict::Parser::UnmergeableFile)
|
|
|
|
conflict_for_path('files/ruby/regex.rb')
|
|
end
|
|
|
|
it 'returns a 404 status code' do
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
end
|
|
|
|
context 'when the file does not exist cannot be resolved in the UI' do
|
|
before do
|
|
conflict_for_path('files/ruby/regexp.rb')
|
|
end
|
|
|
|
it 'returns a 404 status code' do
|
|
expect(response).to have_gitlab_http_status(:not_found)
|
|
end
|
|
end
|
|
|
|
context 'with an existing file' do
|
|
let(:path) { 'files/ruby/regex.rb' }
|
|
|
|
before do
|
|
conflict_for_path(path)
|
|
end
|
|
|
|
it 'returns a 200 status code' do
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
end
|
|
|
|
it 'returns the file in JSON format' do
|
|
content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
|
|
.file_for_path(path, path)
|
|
.content
|
|
|
|
expect(json_response).to include('old_path' => path,
|
|
'new_path' => path,
|
|
'blob_icon' => 'file-text-o',
|
|
'blob_path' => a_string_ending_with(path),
|
|
'content' => content)
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'POST resolve_conflicts' do
|
|
let!(:original_head_sha) { merge_request_with_conflicts.diff_head_sha }
|
|
|
|
def resolve_conflicts(files)
|
|
post :resolve_conflicts,
|
|
namespace_id: merge_request_with_conflicts.project.namespace.to_param,
|
|
project_id: merge_request_with_conflicts.project,
|
|
id: merge_request_with_conflicts.iid,
|
|
format: 'json',
|
|
files: files,
|
|
commit_message: 'Commit message'
|
|
end
|
|
|
|
context 'with valid params' do
|
|
before do
|
|
resolved_files = [
|
|
{
|
|
'new_path' => 'files/ruby/popen.rb',
|
|
'old_path' => 'files/ruby/popen.rb',
|
|
'sections' => {
|
|
'2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_14' => 'head'
|
|
}
|
|
}, {
|
|
'new_path' => 'files/ruby/regex.rb',
|
|
'old_path' => 'files/ruby/regex.rb',
|
|
'sections' => {
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21' => 'origin',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_49_49' => 'origin'
|
|
}
|
|
}
|
|
]
|
|
|
|
resolve_conflicts(resolved_files)
|
|
end
|
|
|
|
it 'creates a new commit on the branch' do
|
|
expect(original_head_sha).not_to eq(merge_request_with_conflicts.source_branch_head.sha)
|
|
expect(merge_request_with_conflicts.source_branch_head.message).to include('Commit message')
|
|
end
|
|
|
|
it 'returns an OK response' do
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
end
|
|
end
|
|
|
|
context 'when sections are missing' do
|
|
before do
|
|
resolved_files = [
|
|
{
|
|
'new_path' => 'files/ruby/popen.rb',
|
|
'old_path' => 'files/ruby/popen.rb',
|
|
'sections' => {
|
|
'2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_14' => 'head'
|
|
}
|
|
}, {
|
|
'new_path' => 'files/ruby/regex.rb',
|
|
'old_path' => 'files/ruby/regex.rb',
|
|
'sections' => {
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head'
|
|
}
|
|
}
|
|
]
|
|
|
|
resolve_conflicts(resolved_files)
|
|
end
|
|
|
|
it 'returns a 400 error' do
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
|
end
|
|
|
|
it 'has a message with the name of the first missing section' do
|
|
expect(json_response['message']).to include('6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21')
|
|
end
|
|
|
|
it 'does not create a new commit' do
|
|
expect(original_head_sha).to eq(merge_request_with_conflicts.source_branch_head.sha)
|
|
end
|
|
end
|
|
|
|
context 'when files are missing' do
|
|
before do
|
|
resolved_files = [
|
|
{
|
|
'new_path' => 'files/ruby/regex.rb',
|
|
'old_path' => 'files/ruby/regex.rb',
|
|
'sections' => {
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21' => 'origin',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_49_49' => 'origin'
|
|
}
|
|
}
|
|
]
|
|
|
|
resolve_conflicts(resolved_files)
|
|
end
|
|
|
|
it 'returns a 400 error' do
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
|
end
|
|
|
|
it 'has a message with the name of the missing file' do
|
|
expect(json_response['message']).to include('files/ruby/popen.rb')
|
|
end
|
|
|
|
it 'does not create a new commit' do
|
|
expect(original_head_sha).to eq(merge_request_with_conflicts.source_branch_head.sha)
|
|
end
|
|
end
|
|
|
|
context 'when a file has identical content to the conflict' do
|
|
before do
|
|
content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
|
|
.file_for_path('files/ruby/popen.rb', 'files/ruby/popen.rb')
|
|
.content
|
|
|
|
resolved_files = [
|
|
{
|
|
'new_path' => 'files/ruby/popen.rb',
|
|
'old_path' => 'files/ruby/popen.rb',
|
|
'content' => content
|
|
}, {
|
|
'new_path' => 'files/ruby/regex.rb',
|
|
'old_path' => 'files/ruby/regex.rb',
|
|
'sections' => {
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21' => 'origin',
|
|
'6eb14e00385d2fb284765eb1cd8d420d33d63fc9_49_49' => 'origin'
|
|
}
|
|
}
|
|
]
|
|
|
|
resolve_conflicts(resolved_files)
|
|
end
|
|
|
|
it 'returns a 400 error' do
|
|
expect(response).to have_gitlab_http_status(:bad_request)
|
|
end
|
|
|
|
it 'has a message with the path of the problem file' do
|
|
expect(json_response['message']).to include('files/ruby/popen.rb')
|
|
end
|
|
|
|
it 'does not create a new commit' do
|
|
expect(original_head_sha).to eq(merge_request_with_conflicts.source_branch_head.sha)
|
|
end
|
|
end
|
|
end
|
|
end
|