summaryrefslogtreecommitdiff
path: root/spec/graphql/mutations
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
commit8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch)
treea77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/graphql/mutations
parent00b35af3db1abfe813a778f643dad221aad51fca (diff)
downloadgitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/graphql/mutations')
-rw-r--r--spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb167
-rw-r--r--spec/graphql/mutations/alert_management/create_alert_issue_spec.rb2
-rw-r--r--spec/graphql/mutations/alert_management/update_alert_status_spec.rb4
-rw-r--r--spec/graphql/mutations/branches/create_spec.rb2
-rw-r--r--spec/graphql/mutations/commits/create_spec.rb180
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb2
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb2
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb22
-rw-r--r--spec/graphql/mutations/container_expiration_policies/update_spec.rb96
-rw-r--r--spec/graphql/mutations/design_management/delete_spec.rb2
-rw-r--r--spec/graphql/mutations/design_management/upload_spec.rb2
-rw-r--r--spec/graphql/mutations/discussions/toggle_resolve_spec.rb155
-rw-r--r--spec/graphql/mutations/issues/set_confidential_spec.rb2
-rw-r--r--spec/graphql/mutations/issues/set_due_date_spec.rb2
-rw-r--r--spec/graphql/mutations/merge_requests/create_spec.rb87
15 files changed, 695 insertions, 32 deletions
diff --git a/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
new file mode 100644
index 00000000000..a025b3d344a
--- /dev/null
+++ b/spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb
@@ -0,0 +1,167 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::AlertManagement::Alerts::SetAssignees do
+ let_it_be(:starting_assignee) { create(:user) }
+ let_it_be(:unassigned_user) { create(:user) }
+ let_it_be(:alert) { create(:alert_management_alert, assignees: [starting_assignee]) }
+ let_it_be(:project) { alert.project }
+
+ let(:current_user) { starting_assignee }
+ let(:assignee_usernames) { [unassigned_user.username] }
+ let(:operation_mode) { nil }
+
+ let(:args) do
+ {
+ project_path: project.full_path,
+ iid: alert.iid,
+ assignee_usernames: assignee_usernames,
+ operation_mode: operation_mode
+ }
+ end
+
+ before_all do
+ project.add_developer(starting_assignee)
+ project.add_developer(unassigned_user)
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:update_alert_management_alert) }
+
+ describe '#resolve' do
+ let(:expected_assignees) { [unassigned_user] }
+
+ subject(:resolve) { mutation_for(project, current_user).resolve(args) }
+
+ shared_examples 'successful resolution' do
+ after do
+ alert.assignees = [starting_assignee]
+ end
+
+ it 'successfully resolves' do
+ expect(resolve).to eq(alert: alert.reload, errors: [])
+ expect(alert.assignees).to eq(expected_assignees)
+ end
+ end
+
+ shared_examples 'noop' do
+ it 'makes no changes' do
+ original_assignees = alert.assignees
+
+ expect(resolve).to eq(alert: alert.reload, errors: [])
+ expect(alert.assignees).to eq(original_assignees)
+ end
+ end
+
+ context 'when operation mode is not specified' do
+ it_behaves_like 'successful resolution'
+ end
+
+ context 'when user does not have permission to update alerts' do
+ let(:current_user) { create(:user) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'for APPEND operation' do
+ let(:operation_mode) { Types::MutationOperationModeEnum.enum[:append] }
+
+ # Only allow a single assignee
+ context 'when a different user is already assigned' do
+ it_behaves_like 'noop'
+ end
+
+ context 'when no users are specified' do
+ let(:assignee_usernames) { [] }
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when a user is specified and no user is assigned' do
+ before do
+ alert.assignees = []
+ end
+
+ it_behaves_like 'successful resolution'
+ end
+
+ context 'when the specified user is already assigned to the alert' do
+ let(:assignee_usernames) { [starting_assignee.username] }
+
+ it_behaves_like 'noop'
+ end
+ end
+
+ context 'for REPLACE operation' do
+ let(:operation_mode) { Types::MutationOperationModeEnum.enum[:replace] }
+
+ context 'when a different user is already assigned' do
+ it_behaves_like 'successful resolution'
+ end
+
+ context 'when no users are specified' do
+ let(:assignee_usernames) { [] }
+ let(:expected_assignees) { [] }
+
+ it_behaves_like 'successful resolution'
+ end
+
+ context 'when a user is specified and no user is assigned' do
+ before do
+ alert.assignees = []
+ end
+
+ it_behaves_like 'successful resolution'
+ end
+
+ context 'when the specified user is already assigned to the alert' do
+ let(:assignee_usernames) { [starting_assignee.username] }
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when multiple users are specified' do
+ let(:assignees) { [starting_assignee, unassigned_user] }
+ let(:assignee_usernames) { assignees.map(&:username) }
+ let(:expected_assignees) { [assignees.last] }
+
+ it_behaves_like 'successful resolution'
+ end
+ end
+
+ context 'for REMOVE operation' do
+ let(:operation_mode) { Types::MutationOperationModeEnum.enum[:remove] }
+
+ context 'when a different user is already assigned' do
+ it_behaves_like 'noop'
+ end
+
+ context 'when no users are specified' do
+ let(:assignee_usernames) { [] }
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when a user is specified and no user is assigned' do
+ before do
+ alert.assignees = []
+ end
+
+ it_behaves_like 'noop'
+ end
+
+ context 'when the specified user is already assigned to the alert' do
+ let(:assignee_usernames) { [starting_assignee.username] }
+ let(:expected_assignees) { [] }
+
+ it_behaves_like 'successful resolution'
+ end
+ end
+ end
+
+ def mutation_for(project, user)
+ described_class.new(object: project, context: { current_user: user }, field: nil)
+ end
+end
diff --git a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
index 1e51767cf0e..fa5a84b4fcc 100644
--- a/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
+++ b/spec/graphql/mutations/alert_management/create_alert_issue_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::AlertManagement::CreateAlertIssue do
+RSpec.describe Mutations::AlertManagement::CreateAlertIssue do
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:alert) { create(:alert_management_alert, project: project, status: 'triggered') }
diff --git a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
index 8b9abd9497d..68513c02040 100644
--- a/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
+++ b/spec/graphql/mutations/alert_management/update_alert_status_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::AlertManagement::UpdateAlertStatus do
+RSpec.describe Mutations::AlertManagement::UpdateAlertStatus do
let_it_be(:current_user) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, :triggered) }
let_it_be(:project) { alert.project }
@@ -33,7 +33,7 @@ describe Mutations::AlertManagement::UpdateAlertStatus do
context 'error occurs when updating' do
it 'returns the alert with errors' do
# Stub an error on the alert
- allow_next_instance_of(Resolvers::AlertManagementAlertResolver) do |resolver|
+ allow_next_instance_of(Resolvers::AlertManagement::AlertResolver) do |resolver|
allow(resolver).to receive(:resolve).and_return(alert)
end
diff --git a/spec/graphql/mutations/branches/create_spec.rb b/spec/graphql/mutations/branches/create_spec.rb
index 744f8f1f2bc..e378a8e3d41 100644
--- a/spec/graphql/mutations/branches/create_spec.rb
+++ b/spec/graphql/mutations/branches/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::Branches::Create do
+RSpec.describe Mutations::Branches::Create do
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb
new file mode 100644
index 00000000000..bb0b8c577b0
--- /dev/null
+++ b/spec/graphql/mutations/commits/create_spec.rb
@@ -0,0 +1,180 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Commits::Create do
+ subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
+
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { create(:user) }
+ let(:context) do
+ GraphQL::Query::Context.new(
+ query: OpenStruct.new(schema: nil),
+ values: { current_user: user },
+ object: nil
+ )
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:push_code) }
+
+ describe '#resolve' do
+ subject { mutation.resolve(project_path: project.full_path, branch: branch, message: message, actions: actions) }
+
+ let(:branch) { 'master' }
+ let(:message) { 'Commit message' }
+ let(:actions) do
+ [
+ {
+ action: 'create',
+ file_path: 'NEW_FILE.md',
+ content: 'Hello'
+ }
+ ]
+ end
+
+ let(:mutated_commit) { subject[:commit] }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when user does not have enough permissions' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when user is a maintainer of a different project' do
+ before do
+ create(:project_empty_repo).add_maintainer(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user can create a commit' do
+ let(:deltas) { mutated_commit.raw_deltas }
+
+ before_all do
+ project.add_developer(user)
+ end
+
+ context 'when service successfully creates a new commit' do
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([
+ a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: 'NEW_FILE.md')
+ ])
+ end
+ end
+
+ context 'when request has multiple actions' do
+ let(:actions) do
+ [
+ {
+ action: 'create',
+ file_path: 'foo/foobar',
+ content: 'some content'
+ },
+ {
+ action: 'delete',
+ file_path: 'README.md'
+ },
+ {
+ action: 'move',
+ file_path: "LICENSE.md",
+ previous_path: "LICENSE",
+ content: "some content"
+ },
+ {
+ action: 'update',
+ file_path: 'VERSION',
+ content: 'new content'
+ },
+ {
+ action: 'chmod',
+ file_path: 'CHANGELOG',
+ execute_filemode: true
+ }
+ ]
+ end
+
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([
+ a_hash_including(a_mode: '0', b_mode: '100644', new_path: 'foo/foobar'),
+ a_hash_including(deleted_file: true, new_path: 'README.md'),
+ a_hash_including(deleted_file: true, new_path: 'LICENSE'),
+ a_hash_including(new_file: true, new_path: 'LICENSE.md'),
+ a_hash_including(new_file: false, new_path: 'VERSION'),
+ a_hash_including(a_mode: '100644', b_mode: '100755', new_path: 'CHANGELOG')
+ ])
+ end
+ end
+
+ context 'when actions are not defined' do
+ let(:actions) { [] }
+
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([])
+ end
+ end
+
+ context 'when branch does not exist' do
+ let(:branch) { 'unknown' }
+
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to eq(['You can only create or edit files when you are on a branch'])
+ end
+ end
+
+ context 'when message is not set' do
+ let(:message) { nil }
+
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to eq(['3:UserCommitFiles: empty CommitMessage'])
+ end
+ end
+
+ context 'when actions are incorrect' do
+ let(:actions) { [{ action: 'unknown', file_path: 'test.md', content: '' }] }
+
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to eq(['Unknown action \'unknown\''])
+ end
+ end
+
+ context 'when branch is protected' do
+ before do
+ create(:protected_branch, project: project, name: branch)
+ end
+
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to eq(['You are not allowed to push into this branch'])
+ end
+ end
+ end
+ end
+
+ def expect_to_contain_deltas(expected_deltas)
+ expect(deltas.count).to eq(expected_deltas.count)
+ expect(deltas).to include(*expected_deltas)
+ end
+end
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb
index 51d3c4f5d6b..6bed3a752ed 100644
--- a/spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb
+++ b/spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::ResolvesGroup do
+RSpec.describe Mutations::ResolvesGroup do
let(:mutation_class) do
Class.new(Mutations::BaseMutation) do
include Mutations::ResolvesGroup
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb
index 145e42e2a51..706a54931ea 100644
--- a/spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb
+++ b/spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::ResolvesIssuable do
+RSpec.describe Mutations::ResolvesIssuable do
let_it_be(:mutation_class) do
Class.new(Mutations::BaseMutation) do
include Mutations::ResolvesIssuable
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
deleted file mode 100644
index b5c349f6284..00000000000
--- a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Mutations::ResolvesProject do
- let(:mutation_class) do
- Class.new(Mutations::BaseMutation) do
- include Mutations::ResolvesProject
- end
- end
-
- let(:context) { double }
-
- subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
-
- it 'uses the ProjectsResolver to resolve projects by path' do
- project = create(:project)
-
- expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context, field: nil).and_call_original
- expect(mutation.resolve_project(full_path: project.full_path).sync).to eq(project)
- end
-end
diff --git a/spec/graphql/mutations/container_expiration_policies/update_spec.rb b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
new file mode 100644
index 00000000000..fc90f437576
--- /dev/null
+++ b/spec/graphql/mutations/container_expiration_policies/update_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::ContainerExpirationPolicies::Update do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:project, reload: true) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ let(:container_expiration_policy) { project.container_expiration_policy }
+ let(:params) { { project_path: project.full_path, cadence: '3month', keep_n: 100, older_than: '14d' } }
+
+ specify { expect(described_class).to require_graphql_authorizations(:destroy_container_image) }
+
+ describe '#resolve' do
+ subject { described_class.new(object: project, context: { current_user: user }, field: nil).resolve(params) }
+
+ RSpec.shared_examples 'returning a success' do
+ it 'returns the container expiration policy with no errors' do
+ expect(subject).to eq(
+ container_expiration_policy: container_expiration_policy,
+ errors: []
+ )
+ end
+ end
+
+ RSpec.shared_examples 'updating the container expiration policy' do
+ it_behaves_like 'updating the container expiration policy attributes', mode: :update, from: { cadence: '1d', keep_n: 10, older_than: '90d' }, to: { cadence: '3month', keep_n: 100, older_than: '14d' }
+
+ it_behaves_like 'returning a success'
+
+ context 'with invalid params' do
+ let_it_be(:params) { { project_path: project.full_path, cadence: '20d' } }
+
+ it_behaves_like 'not creating the container expiration policy'
+
+ it "doesn't update the cadence" do
+ expect { subject }
+ .not_to change { container_expiration_policy.reload.cadence }
+ end
+
+ it 'returns an error' do
+ expect(subject).to eq(
+ container_expiration_policy: nil,
+ errors: ['Cadence is not included in the list']
+ )
+ end
+ end
+ end
+
+ RSpec.shared_examples 'denying access to container expiration policy' do
+ it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'with existing container expiration policy' do
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'updating the container expiration policy'
+ :developer | 'updating the container expiration policy'
+ :reporter | 'denying access to container expiration policy'
+ :guest | 'denying access to container expiration policy'
+ :anonymous | 'denying access to container expiration policy'
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+
+ context 'without existing container expiration policy' do
+ let_it_be(:project, reload: true) { create(:project, :without_container_expiration_policy) }
+
+ where(:user_role, :shared_examples_name) do
+ :maintainer | 'creating the container expiration policy'
+ :developer | 'creating the container expiration policy'
+ :reporter | 'denying access to container expiration policy'
+ :guest | 'denying access to container expiration policy'
+ :anonymous | 'denying access to container expiration policy'
+ end
+
+ with_them do
+ before do
+ project.send("add_#{user_role}", user) unless user_role == :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/design_management/delete_spec.rb b/spec/graphql/mutations/design_management/delete_spec.rb
index 60be6dad62a..3efa865c64b 100644
--- a/spec/graphql/mutations/design_management/delete_spec.rb
+++ b/spec/graphql/mutations/design_management/delete_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::DesignManagement::Delete do
+RSpec.describe Mutations::DesignManagement::Delete do
include DesignManagementTestHelpers
let(:issue) { create(:issue) }
diff --git a/spec/graphql/mutations/design_management/upload_spec.rb b/spec/graphql/mutations/design_management/upload_spec.rb
index 783af70448c..326d88cea80 100644
--- a/spec/graphql/mutations/design_management/upload_spec.rb
+++ b/spec/graphql/mutations/design_management/upload_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-describe Mutations::DesignManagement::Upload do
+RSpec.describe Mutations::DesignManagement::Upload do
include DesignManagementTestHelpers
include ConcurrentHelpers
diff --git a/spec/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
new file mode 100644
index 00000000000..9ac4d6ab165
--- /dev/null
+++ b/spec/graphql/mutations/discussions/toggle_resolve_spec.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Discussions::ToggleResolve do
+ subject(:mutation) do
+ described_class.new(object: nil, context: { current_user: user }, field: nil)
+ end
+
+ let_it_be(:project) { create(:project, :repository) }
+
+ describe '#resolve' do
+ subject do
+ mutation.resolve({ id: id_arg, resolve: resolve_arg })
+ end
+
+ let(:id_arg) { discussion.to_global_id.to_s }
+ let(:resolve_arg) { true }
+ let(:mutated_discussion) { subject[:discussion] }
+ let(:errors) { subject[:errors] }
+
+ shared_examples 'a working resolve method' do
+ context 'when the user does not have permission' do
+ let_it_be(:user) { create(:user) }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(
+ Gitlab::Graphql::Errors::ResourceNotAvailable,
+ "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ )
+ end
+ end
+
+ context 'when the user has permission' do
+ let_it_be(:user) { create(:user, developer_projects: [project]) }
+
+ context 'when discussion cannot be found' do
+ let(:id_arg) { "#{discussion.to_global_id}foo" }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ Gitlab::Graphql::Errors::ResourceNotAvailable,
+ "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
+ )
+ end
+ end
+
+ context 'when discussion is not a Discussion' do
+ let(:discussion) { create(:note, noteable: noteable, project: project) }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ Gitlab::Graphql::Errors::ArgumentError,
+ "#{discussion.to_global_id} is not a valid id for Discussion."
+ )
+ end
+ end
+
+ shared_examples 'returns a resolved discussion without errors' do
+ it 'returns a resolved discussion' do
+ expect(mutated_discussion).to be_resolved
+ end
+
+ it 'returns empty errors' do
+ expect(errors).to be_empty
+ end
+ end
+
+ shared_examples 'returns an unresolved discussion without errors' do
+ it 'returns an unresolved discussion' do
+ expect(mutated_discussion).not_to be_resolved
+ end
+
+ it 'returns empty errors' do
+ expect(errors).to be_empty
+ end
+ end
+
+ context 'when the `resolve` argument is true' do
+ include_examples 'returns a resolved discussion without errors'
+
+ context 'when the discussion is already resolved' do
+ before do
+ discussion.resolve!(user)
+ end
+
+ include_examples 'returns a resolved discussion without errors'
+ end
+
+ context 'when the service raises an `ActiveRecord::RecordNotSaved` error' do
+ before do
+ allow_next_instance_of(::Discussions::ResolveService) do |service|
+ allow(service).to receive(:execute).and_raise(ActiveRecord::RecordNotSaved)
+ end
+ end
+
+ it 'does not resolve the discussion' do
+ expect(mutated_discussion).not_to be_resolved
+ end
+
+ it 'returns errors' do
+ expect(errors).to contain_exactly('Discussion failed to be resolved')
+ end
+ end
+ end
+
+ context 'when the `resolve` argument is false' do
+ let(:resolve_arg) { false }
+
+ context 'when the discussion is resolved' do
+ before do
+ discussion.resolve!(user)
+ end
+
+ include_examples 'returns an unresolved discussion without errors'
+
+ context 'when the service raises an `ActiveRecord::RecordNotSaved` error' do
+ before do
+ allow_next_instance_of(discussion.class) do |instance|
+ allow(instance).to receive(:unresolve!).and_raise(ActiveRecord::RecordNotSaved)
+ end
+ end
+
+ it 'does not unresolve the discussion' do
+ expect(mutated_discussion).to be_resolved
+ end
+
+ it 'returns errors' do
+ expect(errors).to contain_exactly('Discussion failed to be unresolved')
+ end
+ end
+ end
+
+ context 'when the discussion is already unresolved' do
+ include_examples 'returns an unresolved discussion without errors'
+ end
+ end
+ end
+ end
+
+ context 'when discussion is on a merge request' do
+ let_it_be(:noteable) { create(:merge_request, source_project: project) }
+ let(:discussion) { create(:diff_note_on_merge_request, noteable: noteable, project: project).to_discussion }
+
+ it_behaves_like 'a working resolve method'
+ end
+
+ context 'when discussion is on a design' do
+ let_it_be(:noteable) { create(:design, :with_file, issue: create(:issue, project: project)) }
+ let(:discussion) { create(:diff_note_on_design, noteable: noteable, project: project).to_discussion }
+
+ it_behaves_like 'a working resolve method'
+ end
+ end
+end
diff --git a/spec/graphql/mutations/issues/set_confidential_spec.rb b/spec/graphql/mutations/issues/set_confidential_spec.rb
index c90ce2658d6..820f9aa5e17 100644
--- a/spec/graphql/mutations/issues/set_confidential_spec.rb
+++ b/spec/graphql/mutations/issues/set_confidential_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::Issues::SetConfidential do
+RSpec.describe Mutations::Issues::SetConfidential do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
diff --git a/spec/graphql/mutations/issues/set_due_date_spec.rb b/spec/graphql/mutations/issues/set_due_date_spec.rb
index 84df6fce7c7..a638971d966 100644
--- a/spec/graphql/mutations/issues/set_due_date_spec.rb
+++ b/spec/graphql/mutations/issues/set_due_date_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Mutations::Issues::SetDueDate do
+RSpec.describe Mutations::Issues::SetDueDate do
let(:issue) { create(:issue) }
let(:user) { create(:user) }
diff --git a/spec/graphql/mutations/merge_requests/create_spec.rb b/spec/graphql/mutations/merge_requests/create_spec.rb
new file mode 100644
index 00000000000..88acd3ed5b6
--- /dev/null
+++ b/spec/graphql/mutations/merge_requests/create_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Mutations::MergeRequests::Create do
+ subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
+
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:context) do
+ GraphQL::Query::Context.new(
+ query: OpenStruct.new(schema: nil),
+ values: { current_user: user },
+ object: nil
+ )
+ end
+
+ describe '#resolve' do
+ subject do
+ mutation.resolve(
+ project_path: project.full_path,
+ title: title,
+ source_branch: source_branch,
+ target_branch: target_branch,
+ description: description
+ )
+ end
+
+ let(:title) { 'MergeRequest' }
+ let(:source_branch) { 'feature' }
+ let(:target_branch) { 'master' }
+ let(:description) { nil }
+
+ let(:mutated_merge_request) { subject[:merge_request] }
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+
+ context 'when user does not have enough permissions to create a merge request' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'raises an error if the resource is not accessible to the user' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'when the user can create a merge request' do
+ before_all do
+ project.add_developer(user)
+ end
+
+ it 'creates a new merge request' do
+ expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
+ end
+
+ it 'returns a new merge request' do
+ expect(mutated_merge_request.title).to eq(title)
+ expect(subject[:errors]).to be_empty
+ end
+
+ context 'when optional description field is set' do
+ let(:description) { 'content' }
+
+ it 'returns a new merge request with a description' do
+ expect(mutated_merge_request.description).to eq(description)
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when service cannot create a merge request' do
+ let(:title) { nil }
+
+ it 'does not create a new merge request' do
+ expect { mutated_merge_request }.not_to change(MergeRequest, :count)
+ end
+
+ it 'returns errors' do
+ expect(mutated_merge_request).to be_nil
+ expect(subject[:errors]).to eq(['Title can\'t be blank'])
+ end
+ end
+ end
+ end
+end