summaryrefslogtreecommitdiff
path: root/spec/requests
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests')
-rw-r--r--spec/requests/api/ci/job_artifacts_spec.rb80
-rw-r--r--spec/requests/api/ci/jobs_spec.rb92
-rw-r--r--spec/requests/api/ci/pipelines_spec.rb12
-rw-r--r--spec/requests/api/graphql/milestone_spec.rb124
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb17
-rw-r--r--spec/requests/api/graphql/project/milestones_spec.rb21
-rw-r--r--spec/requests/api/members_spec.rb126
-rw-r--r--spec/requests/api/projects_spec.rb7
8 files changed, 372 insertions, 107 deletions
diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb
index 1dd1ca4e115..2fa1ffb4974 100644
--- a/spec/requests/api/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/ci/job_artifacts_spec.rb
@@ -41,42 +41,58 @@ RSpec.describe API::Ci::JobArtifacts do
describe 'DELETE /projects/:id/jobs/:job_id/artifacts' do
let!(:job) { create(:ci_build, :artifacts, pipeline: pipeline, user: api_user) }
- before do
- delete api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
- end
+ context 'when project is not undergoing stats refresh' do
+ before do
+ delete api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
+ end
- context 'when user is anonymous' do
- let(:api_user) { nil }
+ context 'when user is anonymous' do
+ let(:api_user) { nil }
- it 'does not delete artifacts' do
- expect(job.job_artifacts.size).to eq 2
- end
+ it 'does not delete artifacts' do
+ expect(job.job_artifacts.size).to eq 2
+ end
- it 'returns status 401 (unauthorized)' do
- expect(response).to have_gitlab_http_status(:unauthorized)
+ it 'returns status 401 (unauthorized)' do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
end
- end
- context 'with developer' do
- it 'does not delete artifacts' do
- expect(job.job_artifacts.size).to eq 2
+ context 'with developer' do
+ it 'does not delete artifacts' do
+ expect(job.job_artifacts.size).to eq 2
+ end
+
+ it 'returns status 403 (forbidden)' do
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
- it 'returns status 403 (forbidden)' do
- expect(response).to have_gitlab_http_status(:forbidden)
+ context 'with authorized user' do
+ let(:maintainer) { create(:project_member, :maintainer, project: project).user }
+ let!(:api_user) { maintainer }
+
+ it 'deletes artifacts' do
+ expect(job.job_artifacts.size).to eq 0
+ end
+
+ it 'returns status 204 (no content)' do
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
end
end
- context 'with authorized user' do
- let(:maintainer) { create(:project_member, :maintainer, project: project).user }
- let!(:api_user) { maintainer }
+ context 'when project is undergoing stats refresh' do
+ it_behaves_like 'preventing request because of ongoing project stats refresh' do
+ let(:maintainer) { create(:project_member, :maintainer, project: project).user }
+ let(:api_user) { maintainer }
+ let(:make_request) { delete api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user) }
- it 'deletes artifacts' do
- expect(job.job_artifacts.size).to eq 0
- end
+ it 'does not delete artifacts' do
+ make_request
- it 'returns status 204 (no content)' do
- expect(response).to have_gitlab_http_status(:no_content)
+ expect(job.job_artifacts.size).to eq 2
+ end
end
end
end
@@ -131,6 +147,22 @@ RSpec.describe API::Ci::JobArtifacts do
expect(response).to have_gitlab_http_status(:accepted)
end
+
+ context 'when project is undergoing stats refresh' do
+ let!(:job) { create(:ci_build, :artifacts, pipeline: pipeline, user: api_user) }
+
+ it_behaves_like 'preventing request because of ongoing project stats refresh' do
+ let(:maintainer) { create(:project_member, :maintainer, project: project).user }
+ let(:api_user) { maintainer }
+ let(:make_request) { delete api("/projects/#{project.id}/artifacts", api_user) }
+
+ it 'does not delete artifacts' do
+ make_request
+
+ expect(job.job_artifacts.size).to eq 2
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 4bd9f81fd1d..a6cf2dc6d9f 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -655,62 +655,80 @@ RSpec.describe API::Ci::Jobs do
before do
project.add_role(user, role)
-
- post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
- shared_examples_for 'erases job' do
- it 'erases job content' do
- expect(response).to have_gitlab_http_status(:created)
- expect(job.job_artifacts.count).to eq(0)
- expect(job.trace.exist?).to be_falsy
- expect(job.artifacts_file.present?).to be_falsy
- expect(job.artifacts_metadata.present?).to be_falsy
- expect(job.has_job_artifacts?).to be_falsy
+ context 'when project is not undergoing stats refresh' do
+ before do
+ post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
end
- end
- context 'job is erasable' do
- let(:job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, :success, project: project, pipeline: pipeline) }
+ shared_examples_for 'erases job' do
+ it 'erases job content' do
+ expect(response).to have_gitlab_http_status(:created)
+ expect(job.job_artifacts.count).to eq(0)
+ expect(job.trace.exist?).to be_falsy
+ expect(job.artifacts_file.present?).to be_falsy
+ expect(job.artifacts_metadata.present?).to be_falsy
+ expect(job.has_job_artifacts?).to be_falsy
+ end
+ end
+
+ context 'job is erasable' do
+ let(:job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, :success, project: project, pipeline: pipeline) }
- it_behaves_like 'erases job'
+ it_behaves_like 'erases job'
- it 'updates job' do
- job.reload
+ it 'updates job' do
+ job.reload
- expect(job.erased_at).to be_truthy
- expect(job.erased_by).to eq(user)
+ expect(job.erased_at).to be_truthy
+ expect(job.erased_by).to eq(user)
+ end
end
- end
- context 'when job has an unarchived trace artifact' do
- let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, project: project, pipeline: pipeline) }
+ context 'when job has an unarchived trace artifact' do
+ let(:job) { create(:ci_build, :success, :trace_live, :unarchived_trace_artifact, project: project, pipeline: pipeline) }
- it_behaves_like 'erases job'
- end
+ it_behaves_like 'erases job'
+ end
- context 'job is not erasable' do
- let(:job) { create(:ci_build, :trace_live, project: project, pipeline: pipeline) }
+ context 'job is not erasable' do
+ let(:job) { create(:ci_build, :trace_live, project: project, pipeline: pipeline) }
- it 'responds with forbidden' do
- expect(response).to have_gitlab_http_status(:forbidden)
+ it 'responds with forbidden' do
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
- end
- context 'when a developer erases a build' do
- let(:role) { :developer }
- let(:job) { create(:ci_build, :trace_artifact, :artifacts, :success, project: project, pipeline: pipeline, user: owner) }
+ context 'when a developer erases a build' do
+ let(:role) { :developer }
+ let(:job) { create(:ci_build, :trace_artifact, :artifacts, :success, project: project, pipeline: pipeline, user: owner) }
+
+ context 'when the build was created by the developer' do
+ let(:owner) { user }
+
+ it { expect(response).to have_gitlab_http_status(:created) }
+ end
- context 'when the build was created by the developer' do
- let(:owner) { user }
+ context 'when the build was created by another user' do
+ let(:owner) { create(:user) }
- it { expect(response).to have_gitlab_http_status(:created) }
+ it { expect(response).to have_gitlab_http_status(:forbidden) }
+ end
end
+ end
+
+ context 'when project is undergoing stats refresh' do
+ let(:job) { create(:ci_build, :trace_artifact, :artifacts, :test_reports, :success, project: project, pipeline: pipeline) }
+
+ it_behaves_like 'preventing request because of ongoing project stats refresh' do
+ let(:make_request) { post api("/projects/#{project.id}/jobs/#{job.id}/erase", user) }
- context 'when the build was created by the other' do
- let(:owner) { create(:user) }
+ it 'does not delete artifacts' do
+ make_request
- it { expect(response).to have_gitlab_http_status(:forbidden) }
+ expect(job.reload.job_artifacts).not_to be_empty
+ end
end
end
end
diff --git a/spec/requests/api/ci/pipelines_spec.rb b/spec/requests/api/ci/pipelines_spec.rb
index 12faeec94da..697fe16e222 100644
--- a/spec/requests/api/ci/pipelines_spec.rb
+++ b/spec/requests/api/ci/pipelines_spec.rb
@@ -1018,6 +1018,18 @@ RSpec.describe API::Ci::Pipelines do
expect { build.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
+
+ context 'when project is undergoing stats refresh' do
+ it_behaves_like 'preventing request because of ongoing project stats refresh' do
+ let(:make_request) { delete api("/projects/#{project.id}/pipelines/#{pipeline.id}", owner) }
+
+ it 'does not delete the pipeline' do
+ make_request
+
+ expect(pipeline.reload).to be_persisted
+ end
+ end
+ end
end
context 'unauthorized user' do
diff --git a/spec/requests/api/graphql/milestone_spec.rb b/spec/requests/api/graphql/milestone_spec.rb
index 59de116fa2b..f6835936418 100644
--- a/spec/requests/api/graphql/milestone_spec.rb
+++ b/spec/requests/api/graphql/milestone_spec.rb
@@ -5,43 +5,125 @@ require 'spec_helper'
RSpec.describe 'Querying a Milestone' do
include GraphqlHelpers
- let_it_be(:current_user) { create(:user) }
+ let_it_be(:guest) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:release_a) { create(:release, project: project) }
+ let_it_be(:release_b) { create(:release, project: project) }
- let(:query) do
- graphql_query_for('milestone', { id: milestone.to_global_id.to_s }, 'title')
+ before_all do
+ milestone.releases << [release_a, release_b]
+ project.add_guest(guest)
end
- subject { graphql_data['milestone'] }
-
- before do
- post_graphql(query, current_user: current_user)
+ let(:expected_release_nodes) do
+ contain_exactly(a_graphql_entity_for(release_a), a_graphql_entity_for(release_b))
end
- context 'when the user has access to the milestone' do
- before_all do
- project.add_guest(current_user)
+ context 'when we post the query' do
+ let(:current_user) { nil }
+ let(:query) do
+ graphql_query_for('milestone', { id: milestone.to_global_id.to_s }, all_graphql_fields_for('Milestone'))
end
- it_behaves_like 'a working graphql query'
+ subject { graphql_data['milestone'] }
- it { is_expected.to include('title' => milestone.name) }
- end
+ before do
+ post_graphql(query, current_user: current_user)
+ end
- context 'when the user does not have access to the milestone' do
- it_behaves_like 'a working graphql query'
+ context 'when the user has access to the milestone' do
+ let(:current_user) { guest }
- it { is_expected.to be_nil }
+ it_behaves_like 'a working graphql query'
+
+ it { is_expected.to include('title' => milestone.name) }
+
+ it 'contains release information' do
+ is_expected.to include('releases' => include('nodes' => expected_release_nodes))
+ end
+ end
+
+ context 'when the user does not have access to the milestone' do
+ it_behaves_like 'a working graphql query'
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when ID argument is missing' do
+ let(:query) do
+ graphql_query_for('milestone', {}, 'title')
+ end
+
+ it 'raises an exception' do
+ expect(graphql_errors).to include(a_hash_including('message' => "Field 'milestone' is missing required arguments: id"))
+ end
+ end
end
- context 'when ID argument is missing' do
- let(:query) do
- graphql_query_for('milestone', {}, 'title')
+ context 'when there are two milestones' do
+ let_it_be(:milestone_b) { create(:milestone, project: project) }
+
+ let(:current_user) { guest }
+ let(:milestone_fields) do
+ <<~GQL
+ fragment milestoneFields on Milestone {
+ #{all_graphql_fields_for('Milestone', max_depth: 1)}
+ releases { nodes { #{all_graphql_fields_for('Release', max_depth: 1)} } }
+ }
+ GQL
+ end
+
+ let(:single_query) do
+ <<~GQL
+ query ($id_a: MilestoneID!) {
+ a: milestone(id: $id_a) { ...milestoneFields }
+ }
+
+ #{milestone_fields}
+ GQL
+ end
+
+ let(:multi_query) do
+ <<~GQL
+ query ($id_a: MilestoneID!, $id_b: MilestoneID!) {
+ a: milestone(id: $id_a) { ...milestoneFields }
+ b: milestone(id: $id_b) { ...milestoneFields }
+ }
+ #{milestone_fields}
+ GQL
+ end
+
+ it 'produces correct results' do
+ r = run_with_clean_state(multi_query,
+ context: { current_user: current_user },
+ variables: {
+ id_a: global_id_of(milestone).to_s,
+ id_b: milestone_b.to_global_id.to_s
+ })
+
+ expect(r.to_h['errors']).to be_blank
+ expect(graphql_dig_at(r.to_h, :data, :a, :releases, :nodes)).to match expected_release_nodes
+ expect(graphql_dig_at(r.to_h, :data, :b, :releases, :nodes)).to be_empty
end
- it 'raises an exception' do
- expect(graphql_errors).to include(a_hash_including('message' => "Field 'milestone' is missing required arguments: id"))
+ it 'does not suffer from N+1 performance issues' do
+ baseline = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(single_query,
+ context: { current_user: current_user },
+ variables: { id_a: milestone.to_global_id.to_s })
+ end
+
+ multi = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(multi_query,
+ context: { current_user: current_user },
+ variables: {
+ id_a: milestone.to_global_id.to_s,
+ id_b: milestone_b.to_global_id.to_s
+ })
+ end
+
+ expect(multi).not_to exceed_query_limit(baseline)
end
end
end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
index 37656ab4eea..7abd5ca8772 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb
@@ -28,4 +28,21 @@ RSpec.describe 'PipelineDestroy' do
expect(response).to have_gitlab_http_status(:success)
expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
+
+ context 'when project is undergoing stats refresh' do
+ before do
+ create(:project_build_artifacts_size_refresh, :pending, project: pipeline.project)
+ end
+
+ it 'returns an error and does not destroy the pipeline' do
+ expect(Gitlab::ProjectStatsRefreshConflictsLogger)
+ .to receive(:warn_request_rejected_during_stats_refresh)
+ .with(pipeline.project.id)
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_mutation_response(:pipeline_destroy)['errors']).not_to be_empty
+ expect(pipeline.reload).to be_persisted
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb
index 3e8948d83b1..d1ee157fc74 100644
--- a/spec/requests/api/graphql/project/milestones_spec.rb
+++ b/spec/requests/api/graphql/project/milestones_spec.rb
@@ -59,6 +59,27 @@ RSpec.describe 'getting milestone listings nested in a project' do
end
end
+ context 'the user does not have access' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:milestones) { create_list(:milestone, 2, project: project) }
+
+ it 'is nil' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:project)).to be_nil
+ end
+
+ context 'the user has access' do
+ let(:expected) { milestones }
+
+ before do
+ project.add_guest(current_user)
+ end
+
+ it_behaves_like 'searching with parameters'
+ end
+ end
+
context 'there are no search params' do
let(:search_params) { nil }
let(:expected) { all_milestones }
diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb
index 0db42e7439c..94f1bf13830 100644
--- a/spec/requests/api/members_spec.rb
+++ b/spec/requests/api/members_spec.rb
@@ -4,13 +4,14 @@ require 'spec_helper'
RSpec.describe API::Members do
let(:maintainer) { create(:user, username: 'maintainer_user') }
+ let(:maintainer2) { create(:user, username: 'user-with-maintainer-role') }
let(:developer) { create(:user) }
let(:access_requester) { create(:user) }
let(:stranger) { create(:user) }
let(:user_with_minimal_access) { create(:user) }
let(:project) do
- create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
+ create(:project, :public, creator_id: maintainer.id, group: create(:group, :public)) do |project|
project.add_maintainer(maintainer)
project.add_developer(developer, current_user: maintainer)
project.request_access(access_requester)
@@ -238,21 +239,48 @@ RSpec.describe API::Members do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
+
+ context 'adding a member of higher access level' do
+ before do
+ # the other 'maintainer' is in fact an owner of the group!
+ source.add_maintainer(maintainer2)
+ end
+
+ context 'when an access requester' do
+ it 'is not successful' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer2),
+ params: { user_id: access_requester.id, access_level: Member::OWNER }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when a totally new user' do
+ it 'is not successful' do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer2),
+ params: { user_id: stranger.id, access_level: Member::OWNER }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
end
end
- context 'when authenticated as a maintainer/owner' do
+ context 'when authenticated as a member with membership management rights' do
context 'and new member is already a requester' do
- it 'transforms the requester into a proper member' do
- expect do
- post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
- params: { user_id: access_requester.id, access_level: Member::MAINTAINER }
-
- expect(response).to have_gitlab_http_status(:created)
- end.to change { source.members.count }.by(1)
- expect(source.requesters.count).to eq(0)
- expect(json_response['id']).to eq(access_requester.id)
- expect(json_response['access_level']).to eq(Member::MAINTAINER)
+ context 'when the requester is of equal or lower access level' do
+ it 'transforms the requester into a proper member' do
+ expect do
+ post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
+ params: { user_id: access_requester.id, access_level: Member::MAINTAINER }
+
+ expect(response).to have_gitlab_http_status(:created)
+ end.to change { source.members.count }.by(1)
+ expect(source.requesters.count).to eq(0)
+ expect(json_response['id']).to eq(access_requester.id)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
+ end
end
end
@@ -430,7 +458,7 @@ RSpec.describe API::Members do
it 'returns 404 when the user_id is not valid' do
post api("/#{source_type.pluralize}/#{source.id}/members", maintainer),
- params: { user_id: 0, access_level: Member::MAINTAINER }
+ params: { user_id: non_existing_record_id, access_level: Member::MAINTAINER }
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['message']).to eq('404 User Not Found')
@@ -500,16 +528,49 @@ RSpec.describe API::Members do
end
end
end
+
+ context 'as a maintainer updating a member to one with higher access level than themselves' do
+ before do
+ # the other 'maintainer' is in fact an owner of the group!
+ source.add_maintainer(maintainer2)
+ end
+
+ it 'returns 403' do
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer2),
+ params: { access_level: Member::OWNER }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
context 'when authenticated as a maintainer/owner' do
- it 'updates the member' do
- put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
- params: { access_level: Member::MAINTAINER }
+ context 'when updating a member with the same or lower access level' do
+ it 'updates the member' do
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer),
+ params: { access_level: Member::MAINTAINER }
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['id']).to eq(developer.id)
- expect(json_response['access_level']).to eq(Member::MAINTAINER)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(developer.id)
+ expect(json_response['access_level']).to eq(Member::MAINTAINER)
+ end
+ end
+
+ context 'when updating a member with higher access level' do
+ let(:owner) { create(:user) }
+
+ before do
+ source.add_owner(owner)
+ # the other 'maintainer' is in fact an owner of the group!
+ source.add_maintainer(maintainer2)
+ end
+
+ it 'returns 403' do
+ put api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer2),
+ params: { access_level: Member::DEVELOPER }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
end
@@ -604,6 +665,23 @@ RSpec.describe API::Members do
end
end
+ context 'when attempting to delete a member with higher access level' do
+ let(:owner) { create(:user) }
+
+ before do
+ source.add_owner(owner)
+ # the other 'maintainer' is in fact an owner of the group!
+ source.add_maintainer(maintainer2)
+ end
+
+ it 'returns 403' do
+ delete api("/#{source_type.pluralize}/#{source.id}/members/#{owner.id}", maintainer2),
+ params: { access_level: Member::DEVELOPER }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
it 'deletes the member' do
expect do
delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", maintainer)
@@ -679,13 +757,11 @@ RSpec.describe API::Members do
end
context 'adding owner to project' do
- it 'returns created status' do
- expect do
- post api("/projects/#{project.id}/members", maintainer),
- params: { user_id: stranger.id, access_level: Member::OWNER }
+ it 'returns 403' do
+ post api("/projects/#{project.id}/members", maintainer),
+ params: { user_id: stranger.id, access_level: Member::OWNER }
- expect(response).to have_gitlab_http_status(:created)
- end.to change { project.members.count }.by(1)
+ expect(response).to have_gitlab_http_status(:forbidden)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index d2189ab02ea..431d2e56cb5 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -3106,6 +3106,13 @@ RSpec.describe API::Projects do
expect(json_response['error']).to eq 'group_access does not have a valid value'
end
+ it "returns a 400 error when the project-group share is created with an OWNER access level" do
+ post api("/projects/#{project.id}/share", user), params: { group_id: group.id, group_access: Gitlab::Access::OWNER }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq 'group_access does not have a valid value'
+ end
+
it "returns a 409 error when link is not saved" do
allow(::Projects::GroupLinks::CreateService).to receive_message_chain(:new, :execute)
.and_return({ status: :error, http_status: 409, message: 'error' })