2019-07-31 22:56:46 +05:30
|
|
|
# frozen_string_literal: true
|
2016-06-02 11:05:42 +05:30
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe MergeRequests::BuildService do
|
2018-03-17 18:26:18 +05:30
|
|
|
using RSpec::Parameterized::TableSyntax
|
2016-06-02 11:05:42 +05:30
|
|
|
include RepoHelpers
|
2019-01-03 12:48:30 +05:30
|
|
|
include ProjectForksHelper
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
let(:project) { create(:project, :repository) }
|
|
|
|
let(:source_project) { nil }
|
|
|
|
let(:target_project) { nil }
|
2016-06-02 11:05:42 +05:30
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:issue_confidential) { false }
|
|
|
|
let(:issue) { create(:issue, project: project, title: 'A bug', confidential: issue_confidential) }
|
|
|
|
let(:description) { nil }
|
|
|
|
let(:source_branch) { 'feature-branch' }
|
|
|
|
let(:target_branch) { 'master' }
|
2018-11-20 20:47:30 +05:30
|
|
|
let(:milestone_id) { nil }
|
|
|
|
let(:label_ids) { [] }
|
2016-06-02 11:05:42 +05:30
|
|
|
let(:merge_request) { service.execute }
|
|
|
|
let(:compare) { double(:compare, commits: commits) }
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:commit_1) do
|
2022-10-11 01:57:18 +05:30
|
|
|
double(:commit_1,
|
|
|
|
sha: 'f00ba6',
|
|
|
|
safe_message: 'Initial commit',
|
|
|
|
gitaly_commit?: false,
|
|
|
|
id: 'f00ba6',
|
|
|
|
parent_ids: ['f00ba5'])
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
let(:commit_2) do
|
2022-10-11 01:57:18 +05:30
|
|
|
double(:commit_2,
|
|
|
|
sha: 'f00ba7',
|
|
|
|
safe_message: "Closes #1234 Second commit\n\nCreate the app",
|
|
|
|
gitaly_commit?: false,
|
|
|
|
id: 'f00ba7',
|
|
|
|
parent_ids: ['f00ba6'])
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
let(:commit_3) do
|
2022-10-11 01:57:18 +05:30
|
|
|
double(:commit_3,
|
|
|
|
sha: 'f00ba8',
|
|
|
|
safe_message: 'This is a bad commit message!',
|
|
|
|
gitaly_commit?: false,
|
|
|
|
id: 'f00ba8',
|
|
|
|
parent_ids: ['f00ba7'])
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
2016-06-02 11:05:42 +05:30
|
|
|
let(:commits) { nil }
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
let(:params) do
|
|
|
|
{
|
|
|
|
description: description,
|
|
|
|
source_branch: source_branch,
|
|
|
|
target_branch: target_branch,
|
|
|
|
source_project: source_project,
|
|
|
|
target_project: target_project,
|
|
|
|
milestone_id: milestone_id,
|
|
|
|
label_ids: label_ids
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2016-06-02 11:05:42 +05:30
|
|
|
let(:service) do
|
2021-06-08 01:23:25 +05:30
|
|
|
described_class.new(project: project, current_user: user, params: params)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
2018-03-17 18:26:18 +05:30
|
|
|
project.add_guest(user)
|
|
|
|
end
|
2017-01-15 13:20:01 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def stub_compare
|
2016-06-02 11:05:42 +05:30
|
|
|
allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare)
|
2017-08-17 22:00:37 +05:30
|
|
|
allow(project).to receive(:commit).and_return(commit_1)
|
|
|
|
allow(project).to receive(:commit).and_return(commit_2)
|
2021-04-17 20:07:23 +05:30
|
|
|
allow(project).to receive(:commit).and_return(commit_3)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
shared_examples 'allows the merge request to be created' do
|
|
|
|
it do
|
|
|
|
expect(merge_request.can_be_created).to eq(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'forbids the merge request from being created' do
|
|
|
|
it 'returns that the merge request cannot be created' do
|
|
|
|
expect(merge_request.can_be_created).to eq(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'adds an error message to the merge request' do
|
|
|
|
expect(merge_request.errors).to contain_exactly(*Array(error_message))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-23 23:45:48 +05:30
|
|
|
shared_examples 'with a Default.md template' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'the template description is preferred' do
|
|
|
|
expect(merge_request.description).to eq('Default template contents')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
describe '#execute' do
|
|
|
|
it 'calls the compare service with the correct arguments' do
|
2019-01-03 12:48:30 +05:30
|
|
|
allow_any_instance_of(described_class).to receive(:projects_and_branches_valid?).and_return(true)
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(CompareService).to receive(:new)
|
|
|
|
.with(project, Gitlab::Git::BRANCH_REF_PREFIX + source_branch)
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
expect_any_instance_of(CompareService).to receive(:execute)
|
|
|
|
.with(project, Gitlab::Git::BRANCH_REF_PREFIX + target_branch)
|
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
merge_request
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'does not assign force_remove_source_branch' do
|
2019-12-26 22:10:19 +05:30
|
|
|
expect(merge_request.force_remove_source_branch?).to be_truthy
|
2019-02-15 15:39:39 +05:30
|
|
|
end
|
|
|
|
|
2019-10-31 01:37:42 +05:30
|
|
|
context 'with force_remove_source_branch parameter when the user is authorized' do
|
2019-02-15 15:39:39 +05:30
|
|
|
let(:mr_params) { params.merge(force_remove_source_branch: '1') }
|
2019-10-31 01:37:42 +05:30
|
|
|
let(:source_project) { fork_project(project, user) }
|
2021-06-08 01:23:25 +05:30
|
|
|
let(:merge_request) { described_class.new(project: project, current_user: user, params: mr_params).execute }
|
2019-02-15 15:39:39 +05:30
|
|
|
|
2020-11-24 15:15:51 +05:30
|
|
|
before do
|
|
|
|
project.add_reporter(user)
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
it 'assigns force_remove_source_branch' do
|
|
|
|
expect(merge_request.force_remove_source_branch?).to be_truthy
|
|
|
|
end
|
2019-12-26 22:10:19 +05:30
|
|
|
|
|
|
|
context 'with project setting remove_source_branch_after_merge false' do
|
|
|
|
before do
|
|
|
|
project.remove_source_branch_after_merge = false
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'assigns force_remove_source_branch' do
|
|
|
|
expect(merge_request.force_remove_source_branch?).to be_truthy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with project setting remove_source_branch_after_merge true' do
|
|
|
|
before do
|
|
|
|
project.remove_source_branch_after_merge = true
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'assigns force_remove_source_branch' do
|
|
|
|
expect(merge_request.force_remove_source_branch?).to be_truthy
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with force_remove_source_branch parameter false' do
|
|
|
|
before do
|
|
|
|
params[:force_remove_source_branch] = '0'
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not assign force_remove_source_branch' do
|
|
|
|
expect(merge_request.force_remove_source_branch?).to be(false)
|
|
|
|
end
|
|
|
|
end
|
2019-02-15 15:39:39 +05:30
|
|
|
end
|
|
|
|
|
2016-06-02 11:05:42 +05:30
|
|
|
context 'missing source branch' do
|
|
|
|
let(:source_branch) { '' }
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) { 'You must select source and target branch' }
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context 'when target branch is missing' do
|
|
|
|
let(:target_branch) { nil }
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
before do
|
|
|
|
stub_compare
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
context 'when source branch' do
|
|
|
|
context 'is not the repository default branch' do
|
|
|
|
it 'creates compare object with target branch as default branch' do
|
|
|
|
expect(merge_request.compare).to be_present
|
|
|
|
expect(merge_request.target_branch).to eq(project.default_branch)
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'allows the merge request to be created'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'the repository default branch' do
|
|
|
|
let(:source_branch) { 'master' }
|
|
|
|
|
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) { 'You must select source and target branch' }
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
context 'when source project is different from the target project' do
|
|
|
|
let(:target_project) { create(:project, :public, :repository) }
|
|
|
|
let!(:project) { fork_project(target_project, user, namespace: user.namespace, repository: true) }
|
|
|
|
let(:source_project) { project }
|
|
|
|
|
2019-12-26 22:10:19 +05:30
|
|
|
it 'creates compare object with target branch as default branch', :sidekiq_might_not_need_inline do
|
2019-12-04 20:38:33 +05:30
|
|
|
expect(merge_request.compare).to be_present
|
|
|
|
expect(merge_request.target_branch).to eq(project.default_branch)
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'allows the merge request to be created'
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-03 12:29:30 +05:30
|
|
|
context 'same source and target branch' do
|
|
|
|
let(:source_branch) { 'master' }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) { 'You must select different branches' }
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'no commits in the diff' do
|
|
|
|
let(:commits) { [] }
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
before do
|
|
|
|
stub_compare
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'allows the merge request to be created'
|
2016-11-03 12:29:30 +05:30
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
it 'adds a Draft prefix to the merge request title' do
|
|
|
|
expect(merge_request.title).to eq('Draft: Feature branch')
|
2016-11-03 12:29:30 +05:30
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'one commit in the diff' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
|
|
|
let(:commit_description) { commit_2.safe_message.split(/\n+/, 2).last }
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
before do
|
|
|
|
stub_compare
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'allows the merge request to be created'
|
2022-07-23 23:45:48 +05:30
|
|
|
it_behaves_like 'with a Default.md template'
|
2016-06-02 11:05:42 +05:30
|
|
|
|
|
|
|
it 'uses the title of the commit as the title of the merge request' do
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(merge_request.title).to eq(commit_2.safe_message.split("\n").first)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'uses the description of the commit as the description of the merge request' do
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(merge_request.description).to eq(commit_description)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'merge request already has a description set' do
|
|
|
|
let(:description) { 'Merge request description' }
|
|
|
|
|
|
|
|
it 'keeps the description from the initial params' do
|
|
|
|
expect(merge_request.description).to eq(description)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'commit has no description' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:commits) { Commit.decorate([commit_3], project) }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2022-07-23 23:45:48 +05:30
|
|
|
it_behaves_like 'with a Default.md template'
|
|
|
|
|
2016-06-02 11:05:42 +05:30
|
|
|
it 'uses the title of the commit as the title of the merge request' do
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(merge_request.title).to eq(commit_3.safe_message)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the description to nil' do
|
|
|
|
expect(merge_request.description).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'when the source branch matches an issue' do
|
2021-09-04 01:27:46 +05:30
|
|
|
where(:factory, :source_branch, :closing_message) do
|
2021-09-30 23:02:18 +05:30
|
|
|
:jira_integration | 'FOO-123-fix-issue' | 'Closes FOO-123'
|
|
|
|
:jira_integration | 'fix-issue' | nil
|
2021-09-04 01:27:46 +05:30
|
|
|
:custom_issue_tracker_integration | '123-fix-issue' | 'Closes #123'
|
|
|
|
:custom_issue_tracker_integration | 'fix-issue' | nil
|
|
|
|
nil | '123-fix-issue' | 'Closes #123'
|
|
|
|
nil | 'fix-issue' | nil
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
with_them do
|
|
|
|
before do
|
2021-09-04 01:27:46 +05:30
|
|
|
if factory
|
|
|
|
create(factory, project: project)
|
2021-03-11 19:13:27 +05:30
|
|
|
project.reload
|
2021-09-04 01:27:46 +05:30
|
|
|
else
|
|
|
|
issue.update!(iid: 123)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
it 'uses the title of the commit as the title of the merge request' do
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
2018-11-20 20:47:30 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'appends the closing description' do
|
|
|
|
expected_description = [commit_description, closing_message].compact.join("\n\n")
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(merge_request.description).to eq(expected_description)
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
context 'a Default.md template is defined' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'appends the closing description to a Default.md template' do
|
|
|
|
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
|
|
|
|
|
|
|
expect(merge_request.description).to eq(expected_description)
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
2018-11-20 20:47:30 +05:30
|
|
|
|
|
|
|
context 'when the source branch matches an internal issue' do
|
|
|
|
let(:label) { create(:label, project: project) }
|
|
|
|
let(:milestone) { create(:milestone, project: project) }
|
|
|
|
let(:source_branch) { '123-fix-issue' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
issue.update!(iid: 123, labels: [label], milestone: milestone)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'assigns the issue label and milestone' do
|
|
|
|
expect(merge_request.milestone).to eq(milestone)
|
|
|
|
expect(merge_request.labels).to match_array([label])
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when milestone_id and label_ids are shared in the params' do
|
|
|
|
let(:label2) { create(:label, project: project) }
|
|
|
|
let(:milestone2) { create(:milestone, project: project) }
|
|
|
|
let(:label_ids) { [label2.id] }
|
|
|
|
let(:milestone_id) { milestone2.id }
|
|
|
|
|
2019-07-31 22:56:46 +05:30
|
|
|
before do
|
|
|
|
# Guests are not able to assign labels or milestones to an issue
|
|
|
|
project.add_developer(user)
|
|
|
|
end
|
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
it 'assigns milestone_id and label_ids instead of issue labels and milestone' do
|
|
|
|
expect(merge_request.milestone).to eq(milestone2)
|
|
|
|
expect(merge_request.labels).to match_array([label2])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-03-13 22:55:13 +05:30
|
|
|
|
|
|
|
context 'when a milestone is from another project' do
|
|
|
|
let(:milestone) { create(:milestone, project: create(:project)) }
|
|
|
|
let(:milestone_id) { milestone.id }
|
|
|
|
|
|
|
|
it 'sets milestone to nil' do
|
|
|
|
expect(merge_request.milestone).to be_nil
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-17 20:07:23 +05:30
|
|
|
context 'no multi-line commit messages in the diff' do
|
|
|
|
let(:commits) { Commit.decorate([commit_1, commit_3], project) }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
before do
|
|
|
|
stub_compare
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'allows the merge request to be created'
|
2022-07-23 23:45:48 +05:30
|
|
|
it_behaves_like 'with a Default.md template'
|
2016-06-02 11:05:42 +05:30
|
|
|
|
|
|
|
it 'uses the title of the branch as the merge request title' do
|
|
|
|
expect(merge_request.title).to eq('Feature branch')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not add a description' do
|
|
|
|
expect(merge_request.description).to be_nil
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'merge request already has a description set' do
|
|
|
|
let(:description) { 'Merge request description' }
|
|
|
|
|
|
|
|
it 'keeps the description from the initial params' do
|
|
|
|
expect(merge_request.description).to eq(description)
|
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
context 'a Default.md template is defined' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'keeps the description from the initial params' do
|
|
|
|
expect(merge_request.description).to eq(description)
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'when the source branch matches an issue' do
|
2021-09-04 01:27:46 +05:30
|
|
|
where(:factory, :source_branch, :title, :closing_message) do
|
2021-09-30 23:02:18 +05:30
|
|
|
:jira_integration | 'FOO-123-fix-issue' | 'Resolve FOO-123 "Fix issue"' | 'Closes FOO-123'
|
|
|
|
:jira_integration | 'fix-issue' | 'Fix issue' | nil
|
2021-09-04 01:27:46 +05:30
|
|
|
:custom_issue_tracker_integration | '123-fix-issue' | 'Resolve #123 "Fix issue"' | 'Closes #123'
|
|
|
|
:custom_issue_tracker_integration | 'fix-issue' | 'Fix issue' | nil
|
|
|
|
nil | '123-fix-issue' | 'Resolve "A bug"' | 'Closes #123'
|
|
|
|
nil | 'fix-issue' | 'Fix issue' | nil
|
|
|
|
nil | '124-fix-issue' | '124 fix issue' | nil
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
with_them do
|
2017-01-15 13:20:01 +05:30
|
|
|
before do
|
2021-09-04 01:27:46 +05:30
|
|
|
if factory
|
|
|
|
create(factory, project: project)
|
2021-03-11 19:13:27 +05:30
|
|
|
project.reload
|
2021-09-04 01:27:46 +05:30
|
|
|
else
|
|
|
|
issue.update!(iid: 123)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the correct title' do
|
|
|
|
expect(merge_request.title).to eq(title)
|
2017-01-15 13:20:01 +05:30
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it 'sets the closing description' do
|
|
|
|
expect(merge_request.description).to eq(closing_message)
|
2017-01-15 13:20:01 +05:30
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
context 'a Default.md template is defined' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'appends the closing description to a Default.md template' do
|
|
|
|
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
|
|
|
|
|
|
|
expect(merge_request.description).to eq(expected_description)
|
|
|
|
end
|
|
|
|
end
|
2017-01-15 13:20:01 +05:30
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
context 'a multi-line commit message in the diff' do
|
|
|
|
let(:commits) { Commit.decorate([commit_1, commit_2, commit_3], project) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_compare
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'allows the merge request to be created'
|
2022-07-23 23:45:48 +05:30
|
|
|
it_behaves_like 'with a Default.md template'
|
2021-04-17 20:07:23 +05:30
|
|
|
|
|
|
|
it 'uses the first line of the first multi-line commit message as the title' do
|
|
|
|
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'adds the remaining lines of the first multi-line commit message as the description' do
|
|
|
|
expect(merge_request.description).to eq('Create the app')
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when the source branch matches an issue' do
|
2021-09-04 01:27:46 +05:30
|
|
|
where(:factory, :source_branch, :title, :closing_message) do
|
2021-09-30 23:02:18 +05:30
|
|
|
:jira_integration | 'FOO-123-fix-issue' | 'Resolve FOO-123 "Fix issue"' | 'Closes FOO-123'
|
|
|
|
:jira_integration | 'fix-issue' | 'Fix issue' | nil
|
2021-09-04 01:27:46 +05:30
|
|
|
:custom_issue_tracker_integration | '123-fix-issue' | 'Resolve #123 "Fix issue"' | 'Closes #123'
|
|
|
|
:custom_issue_tracker_integration | 'fix-issue' | 'Fix issue' | nil
|
|
|
|
nil | '123-fix-issue' | 'Resolve "A bug"' | 'Closes #123'
|
|
|
|
nil | 'fix-issue' | 'Fix issue' | nil
|
|
|
|
nil | '124-fix-issue' | '124 fix issue' | nil
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
with_them do
|
|
|
|
before do
|
2021-09-04 01:27:46 +05:30
|
|
|
if factory
|
|
|
|
create(factory, project: project)
|
2021-04-17 20:07:23 +05:30
|
|
|
project.reload
|
2021-09-04 01:27:46 +05:30
|
|
|
else
|
|
|
|
issue.update!(iid: 123)
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the correct title' do
|
|
|
|
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the closing description' do
|
|
|
|
expect(merge_request.description).to eq("Create the app#{closing_message ? "\n\n" + closing_message : ''}")
|
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
context 'a Default.md template is defined' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'appends the closing description to a Default.md template' do
|
|
|
|
expected_description = ['Default template contents', closing_message].compact.join("\n\n")
|
|
|
|
|
|
|
|
expect(merge_request.description).to eq(expected_description)
|
|
|
|
end
|
|
|
|
end
|
2021-04-17 20:07:23 +05:30
|
|
|
end
|
|
|
|
end
|
2017-01-15 13:20:01 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'when the issue is not accessible to user' do
|
|
|
|
let(:source_branch) { "#{issue.iid}-fix-issue" }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
before do
|
|
|
|
project.team.truncate
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
|
2021-04-17 20:07:23 +05:30
|
|
|
it 'uses the first line of the first multi-line commit message as the title' do
|
|
|
|
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2021-11-18 22:05:49 +05:30
|
|
|
it 'adds the remaining lines of the first multi-line commit message as the description', :sidekiq_inline do
|
2021-04-17 20:07:23 +05:30
|
|
|
expect(merge_request.description).to eq('Create the app')
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'when the issue is confidential' do
|
|
|
|
let(:source_branch) { "#{issue.iid}-fix-issue" }
|
|
|
|
let(:issue_confidential) { true }
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2021-04-17 20:07:23 +05:30
|
|
|
it 'uses the first line of the first multi-line commit message as the title' do
|
|
|
|
expect(merge_request.title).to eq('Closes #1234 Second commit')
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
|
2021-04-17 20:07:23 +05:30
|
|
|
it 'adds the remaining lines of the first multi-line commit message as the description' do
|
|
|
|
expect(merge_request.description).to eq('Create the app')
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
context 'source branch does not exist' do
|
|
|
|
before do
|
|
|
|
allow(project).to receive(:commit).with(source_branch).and_return(nil)
|
2021-04-17 20:07:23 +05:30
|
|
|
allow(project).to receive(:commit).with(target_branch).and_return(commit_2)
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) { 'Source branch "feature-branch" does not exist' }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'target branch does not exist' do
|
|
|
|
before do
|
2021-04-17 20:07:23 +05:30
|
|
|
allow(project).to receive(:commit).with(source_branch).and_return(commit_2)
|
2017-08-17 22:00:37 +05:30
|
|
|
allow(project).to receive(:commit).with(target_branch).and_return(nil)
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) { 'Target branch "master" does not exist' }
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'both source and target branches do not exist' do
|
|
|
|
before do
|
|
|
|
allow(project).to receive(:commit).and_return(nil)
|
|
|
|
end
|
|
|
|
|
2019-12-04 20:38:33 +05:30
|
|
|
it_behaves_like 'forbids the merge request from being created' do
|
|
|
|
let(:error_message) do
|
|
|
|
['Source branch "feature-branch" does not exist', 'Target branch "master" does not exist']
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'upstream project has disabled merge requests' do
|
2017-09-10 17:25:29 +05:30
|
|
|
let(:upstream_project) { create(:project, :merge_requests_disabled) }
|
|
|
|
let(:project) { create(:project, forked_from_project: upstream_project) }
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
it 'sets target project correctly' do
|
|
|
|
expect(merge_request.target_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'target_project is set and accessible by current_user' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:target_project) { create(:project, :public, :repository) }
|
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
it 'sets target project correctly' do
|
|
|
|
expect(merge_request.target_project).to eq(target_project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'target_project is set but not accessible by current_user' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:target_project) { create(:project, :private, :repository) }
|
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
|
|
|
it 'sets target project correctly' do
|
|
|
|
expect(merge_request.target_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-01-03 12:48:30 +05:30
|
|
|
context 'target_project is set but repo is not accessible by current_user' do
|
|
|
|
let(:target_project) do
|
|
|
|
create(:project, :public, :repository, repository_access_level: ProjectFeature::PRIVATE)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets target project correctly' do
|
|
|
|
expect(merge_request.target_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context 'source_project is set and accessible by current_user' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:source_project) { create(:project, :public, :repository) }
|
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-01-03 12:48:30 +05:30
|
|
|
before do
|
|
|
|
# To create merge requests _from_ a project the user needs at least
|
|
|
|
# developer access
|
|
|
|
source_project.add_developer(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets source project correctly' do
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(merge_request.source_project).to eq(source_project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'source_project is set but not accessible by current_user' do
|
2021-04-17 20:07:23 +05:30
|
|
|
let(:source_project) { create(:project, :private, :repository) }
|
|
|
|
let(:commits) { Commit.decorate([commit_2], project) }
|
2017-08-17 22:00:37 +05:30
|
|
|
|
2019-01-03 12:48:30 +05:30
|
|
|
it 'sets source project correctly' do
|
|
|
|
expect(merge_request.source_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'source_project is set but the user cannot create merge requests from the project' do
|
|
|
|
let(:source_project) do
|
|
|
|
create(:project, :public, :repository, merge_requests_access_level: ProjectFeature::PRIVATE)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the source_project correctly' do
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(merge_request.source_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
2018-12-13 13:39:08 +05:30
|
|
|
|
2019-01-03 12:48:30 +05:30
|
|
|
context 'target_project is not in the fork network of source_project' do
|
|
|
|
let(:target_project) { create(:project, :public, :repository) }
|
|
|
|
|
|
|
|
it 'adds an error to the merge request' do
|
|
|
|
expect(merge_request.errors[:validate_fork]).to contain_exactly('Source project is not a fork of the target project')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'target_project is in the fork network of source project but no longer accessible' do
|
|
|
|
let!(:project) { fork_project(target_project, user, namespace: user.namespace, repository: true) }
|
|
|
|
let(:source_project) { project }
|
|
|
|
let(:target_project) { create(:project, :public, :repository) }
|
|
|
|
|
|
|
|
before do
|
2020-11-24 15:15:51 +05:30
|
|
|
target_project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
|
2019-01-03 12:48:30 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the target_project correctly' do
|
|
|
|
expect(merge_request.target_project).to eq(project)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-13 13:39:08 +05:30
|
|
|
context 'when specifying target branch in the description' do
|
|
|
|
let(:description) { "A merge request targeting another branch\n\n/target_branch with-codeowners" }
|
|
|
|
|
|
|
|
it 'sets the attribute from the quick actions' do
|
|
|
|
expect(merge_request.target_branch).to eq('with-codeowners')
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|
2019-07-31 22:56:46 +05:30
|
|
|
|
|
|
|
context 'when assigning labels' do
|
|
|
|
let(:label_ids) { [create(:label, project: project).id] }
|
|
|
|
|
|
|
|
context 'for members with less than developer access' do
|
|
|
|
it 'is not allowed' do
|
|
|
|
expect(merge_request.label_ids).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for users allowed to assign labels' do
|
|
|
|
before do
|
|
|
|
project.add_developer(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for labels in the project' do
|
|
|
|
it 'is allowed for developers' do
|
|
|
|
expect(merge_request.label_ids).to contain_exactly(*label_ids)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'for unrelated labels' do
|
|
|
|
let(:project_label) { create(:label, project: project) }
|
|
|
|
let(:label_ids) { [create(:label).id, project_label.id] }
|
|
|
|
|
|
|
|
it 'only assigns related labels' do
|
|
|
|
expect(merge_request.label_ids).to contain_exactly(project_label.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
describe '#assign_description_from_repository_template' do
|
|
|
|
subject { service.send(:assign_description_from_repository_template) }
|
|
|
|
|
|
|
|
it 'performs no action if the merge request description is not blank' do
|
|
|
|
merge_request.description = 'foo'
|
|
|
|
subject
|
|
|
|
expect(merge_request.description).to eq 'foo'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when a Default template is not found' do
|
|
|
|
it 'does not modify the merge request description' do
|
|
|
|
merge_request.description = nil
|
|
|
|
subject
|
|
|
|
expect(merge_request.description).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when a Default template is found' do
|
|
|
|
context 'when its contents cannot be retrieved' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/OtherTemplate.md' => 'Other template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'does not modify the merge request description' do
|
|
|
|
allow(TemplateFinder).to receive(:all_template_names).and_return({
|
|
|
|
merge_requests: [
|
|
|
|
{ name: 'Default', id: 'default', key: 'default', project_id: project.id }
|
|
|
|
]
|
|
|
|
})
|
|
|
|
|
|
|
|
merge_request.description = nil
|
|
|
|
subject
|
|
|
|
expect(merge_request.description).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when its contents can be retrieved' do
|
|
|
|
let(:files) { { '.gitlab/merge_request_templates/Default.md' => 'Default template contents' } }
|
2023-01-13 00:05:48 +05:30
|
|
|
let(:project) { create(:project, :custom_repo, files: files) }
|
2022-07-23 23:45:48 +05:30
|
|
|
|
|
|
|
it 'modifies the merge request description' do
|
|
|
|
merge_request.description = nil
|
|
|
|
subject
|
|
|
|
expect(merge_request.description).to eq 'Default template contents'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-06-02 11:05:42 +05:30
|
|
|
end
|