summaryrefslogtreecommitdiff
path: root/spec/requests/api/commits_spec.rb
diff options
context:
space:
mode:
authorMarkus Koller <mkoller@gitlab.com>2019-06-13 12:44:41 +0200
committerMarkus Koller <mkoller@gitlab.com>2019-07-18 09:19:18 +0200
commitf8cecafb07792bcaf9d7ffa85766c3b33c1dd252 (patch)
treeabafa6e9dbbb602f61b83abff508acbae074ceee /spec/requests/api/commits_spec.rb
parentb921b2d1fb0c1cb3e6d4f3c88806855b48827855 (diff)
downloadgitlab-ce-f8cecafb07792bcaf9d7ffa85766c3b33c1dd252.tar.gz
Add start_sha to commits API
When passing start_branch on committing from the WebIDE, it's possible that the branch has changed since editing started, which results in the change being applied on top of the latest commit in the branch and overwriting the new changes. By passing the start_sha instead we can make sure that the change is applied on top of the commit which the user started editing from.
Diffstat (limited to 'spec/requests/api/commits_spec.rb')
-rw-r--r--spec/requests/api/commits_spec.rb203
1 files changed, 158 insertions, 45 deletions
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 204e378f7be..311ef3ee99a 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -320,67 +320,132 @@ describe API::Commits do
end
end
- context 'when the API user is a guest' do
+ context 'when committing to a new branch' do
def last_commit_id(project, branch_name)
project.repository.find_branch(branch_name)&.dereferenced_target&.id
end
- let(:public_project) { create(:project, :public, :repository) }
- let!(:url) { "/projects/#{public_project.id}/repository/commits" }
- let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } }
+ before do
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
+ end
- it 'returns a 403' do
- post api(url, guest), params: valid_c_params
+ context 'when the API user is a guest' do
+ let(:public_project) { create(:project, :public, :repository) }
+ let(:url) { "/projects/#{public_project.id}/repository/commits" }
+ let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } }
- expect(response).to have_gitlab_http_status(403)
- end
+ it 'returns a 403' do
+ post api(url, guest), params: valid_c_params
- context 'when start_project is provided' do
- context 'when posting to a forked project the user owns' do
- let!(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) }
- let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
+ expect(response).to have_gitlab_http_status(403)
+ end
- before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
- end
+ context 'when start_project is provided' do
+ context 'when posting to a forked project the user owns' do
+ let(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) }
+ let(:url) { "/projects/#{forked_project.id}/repository/commits" }
+
+ context 'identified by Integer (id)' do
+ before do
+ valid_c_params[:start_project] = public_project.id
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ end
+ end
- context 'identified by Integer (id)' do
- before do
- valid_c_params[:start_project] = public_project.id
+ context 'identified by String (full_path)' do
+ before do
+ valid_c_params[:start_project] = public_project.full_path
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ end
end
- it 'adds a new commit to forked_project and returns a 201' do
- expect { post api(url, guest), params: valid_c_params }
- .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
- .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ context 'when branch already exists' do
+ before do
+ valid_c_params.delete(:start_branch)
+ valid_c_params[:branch] = 'master'
+ valid_c_params[:start_project] = public_project.id
+ end
+
+ it 'returns a 400' do
+ post api(url, guest), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("A branch called 'master' already exists. Switch to that branch in order to make changes")
+ end
+
+ context 'when force is set to true' do
+ before do
+ valid_c_params[:force] = true
+ end
+
+ it 'adds a new commit to forked_project and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(public_project, valid_c_params[:branch]) }
+ end
+ end
+ end
+
+ context 'when start_sha is also provided' do
+ let(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: false) }
+ let(:start_sha) { public_project.repository.commit.parent.sha }
+
+ before do
+ # initialize an empty repository to force fetching from the original project
+ forked_project.repository.create_if_not_exists
- expect(response).to have_gitlab_http_status(201)
+ valid_c_params[:start_project] = public_project.id
+ valid_c_params[:start_sha] = start_sha
+ valid_c_params.delete(:start_branch)
+ end
+
+ it 'fetches the start_sha from the original project to use as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, guest), params: valid_c_params }
+ .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(forked_project, 'master') }
+
+ last_commit = forked_project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
end
end
- context 'identified by String (full_path)' do
+ context 'when the target project is not part of the fork network of start_project' do
+ let(:unrelated_project) { create(:project, :public, :repository, creator: guest) }
+ let(:url) { "/projects/#{unrelated_project.id}/repository/commits" }
+
before do
- valid_c_params[:start_project] = public_project.full_path
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
+ valid_c_params[:start_project] = public_project.id
end
- it 'adds a new commit to forked_project and returns a 201' do
- expect { post api(url, guest), params: valid_c_params }
- .to change { last_commit_id(forked_project, valid_c_params[:branch]) }
- .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) }
+ it 'returns a 403' do
+ post api(url, guest), params: valid_c_params
- expect(response).to have_gitlab_http_status(201)
+ expect(response).to have_gitlab_http_status(403)
end
end
end
- context 'when the target project is not part of the fork network of start_project' do
- let(:unrelated_project) { create(:project, :public, :repository, creator: guest) }
- let!(:url) { "/projects/#{unrelated_project.id}/repository/commits" }
+ context 'when posting to a forked project the user does not have write access' do
+ let(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) }
+ let(:url) { "/projects/#{forked_project.id}/repository/commits" }
before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
+ valid_c_params[:start_branch] = 'master'
+ valid_c_params[:branch] = 'patch'
valid_c_params[:start_project] = public_project.id
end
@@ -392,20 +457,68 @@ describe API::Commits do
end
end
- context 'when posting to a forked project the user does not have write access' do
- let!(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) }
- let!(:url) { "/projects/#{forked_project.id}/repository/commits" }
+ context 'when start_sha is provided' do
+ let(:start_sha) { project.repository.commit.parent.sha }
before do
- valid_c_params[:start_branch] = "master"
- valid_c_params[:branch] = "patch"
- valid_c_params[:start_project] = public_project.id
+ valid_c_params[:start_sha] = start_sha
+ valid_c_params.delete(:start_branch)
end
- it 'returns a 403' do
- post api(url, guest), params: valid_c_params
+ it 'returns a 400 if start_branch is also provided' do
+ valid_c_params[:start_branch] = 'master'
+ post api(url, user), params: valid_c_params
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('start_branch, start_sha are mutually exclusive')
+ end
+
+ it 'returns a 400 if branch already exists' do
+ valid_c_params[:branch] = 'master'
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("A branch called 'master' already exists. Switch to that branch in order to make changes")
+ end
+
+ it 'returns a 400 if start_sha does not exist' do
+ valid_c_params[:start_sha] = '1' * 40
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("Cannot find start_sha '#{valid_c_params[:start_sha]}'")
+ end
+
+ it 'returns a 400 if start_sha is not a full SHA' do
+ valid_c_params[:start_sha] = start_sha.slice(0, 7)
+ post api(url, user), params: valid_c_params
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']).to eq("Invalid start_sha '#{valid_c_params[:start_sha]}'")
+ end
+
+ it 'uses the start_sha as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, user), params: valid_c_params }
+ .to change { last_commit_id(project, valid_c_params[:branch]) }
+ .and not_change { last_commit_id(project, 'master') }
+
+ last_commit = project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
+
+ context 'when force is set to true and branch already exists' do
+ before do
+ valid_c_params[:force] = true
+ valid_c_params[:branch] = 'master'
+ end
+
+ it 'uses the start_sha as parent commit and returns a 201' do
+ expect_request_with_status(201) { post api(url, user), params: valid_c_params }
+ .to change { last_commit_id(project, valid_c_params[:branch]) }
+
+ last_commit = project.repository.find_branch(valid_c_params[:branch]).dereferenced_target
+ expect(last_commit.parent_id).to eq(start_sha)
+ end
end
end
end