summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/graphql/gitlab_schema_spec.rb2
-rw-r--r--spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb19
-rw-r--r--spec/graphql/mutations/merge_requests/set_wip_spec.rb51
-rw-r--r--spec/graphql/types/mutation_type_spec.rb9
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb103
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb68
-rw-r--r--spec/support/helpers/graphql_helpers.rb61
-rw-r--r--spec/support/matchers/graphql_matchers.rb9
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb2
10 files changed, 333 insertions, 11 deletions
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index 515bbe78cb7..b9ddb427e85 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -18,8 +18,6 @@ describe GitlabSchema do
end
it 'has the base mutation' do
- pending('Adding an empty mutation breaks the documentation explorer')
-
expect(described_class.mutation).to eq(::Types::MutationType.to_graphql)
end
diff --git a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
new file mode 100644
index 00000000000..19f5a8907a2
--- /dev/null
+++ b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb
@@ -0,0 +1,19 @@
+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) }
+
+ it 'uses the ProjectsResolver to resolve projects by path' do
+ project = create(:project)
+
+ expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original
+ expect(mutation.resolve_project(full_path: project.full_path)).to eq(project)
+ end
+end
diff --git a/spec/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..e600abf3941
--- /dev/null
+++ b/spec/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe Mutations::MergeRequests::SetWip do
+ let(:merge_request) { create(:merge_request) }
+ let(:user) { create(:user) }
+ subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) }
+
+ describe '#resolve' do
+ let(:wip) { true }
+ let(:mutated_merge_request) { subject[:merge_request] }
+ subject { mutation.resolve(project_path: merge_request.project.full_path, iid: merge_request.iid, wip: wip) }
+
+ 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 the user can update the merge request' do
+ before do
+ merge_request.project.add_developer(user)
+ end
+
+ it 'returns the merge request as a wip' do
+ expect(mutated_merge_request).to eq(merge_request)
+ expect(mutated_merge_request).to be_work_in_progress
+ expect(subject[:errors]).to be_empty
+ end
+
+ it 'returns errors merge request could not be updated' do
+ # Make the merge request invalid
+ merge_request.allow_broken = true
+ merge_request.update!(source_project: nil)
+
+ expect(subject[:errors]).not_to be_empty
+ end
+
+ context 'when passing wip as false' do
+ let(:wip) { false }
+
+ it 'removes `wip` from the title' do
+ merge_request.update(title: "WIP: working on it")
+
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+
+ it 'does not do anything if the title did not start with wip' do
+ expect(mutated_merge_request).not_to be_work_in_progress
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/types/mutation_type_spec.rb b/spec/graphql/types/mutation_type_spec.rb
new file mode 100644
index 00000000000..a67d83b1edf
--- /dev/null
+++ b/spec/graphql/types/mutation_type_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Types::MutationType do
+ it 'is expected to have the MergeRequestSetWip' do
+ expect(described_class).to have_graphql_mutation(Mutations::MergeRequests::SetWip)
+ end
+end
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
new file mode 100644
index 00000000000..95bf7685ade
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize::AuthorizeResource do
+ let(:fake_class) do
+ Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ attr_reader :user, :found_object
+
+ authorize :read_the_thing
+
+ def initialize(user, found_object)
+ @user, @found_object = user, found_object
+ end
+
+ def find_object
+ found_object
+ end
+
+ def current_user
+ user
+ end
+ end
+ end
+
+ let(:user) { build(:user) }
+ let(:project) { build(:project) }
+ subject(:loading_resource) { fake_class.new(user, project) }
+
+ context 'when the user is allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ true
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find).to eq(project)
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'returns the object' do
+ expect(loading_resource.authorized_find!).to eq(project)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.not_to raise_error
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is true' do
+ expect(loading_resource.authorized?(project)).to be(true)
+ end
+ end
+ end
+
+ context 'when the user is not allowed to perform the action' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do
+ false
+ end
+ end
+
+ describe '#authorized_find' do
+ it 'returns `nil`' do
+ expect(loading_resource.authorized_find).to be_nil
+ end
+ end
+
+ describe '#authorized_find!' do
+ it 'raises an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorize!' do
+ it 'does not raise an error' do
+ expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ describe '#authorized?' do
+ it 'is false' do
+ expect(loading_resource.authorized?(project)).to be(false)
+ end
+ end
+ end
+
+ context 'when the class does not define #find_object' do
+ let(:fake_class) do
+ Class.new { include Gitlab::Graphql::Authorize::AuthorizeResource }
+ end
+
+ it 'raises a comprehensive error message' do
+ expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
new file mode 100644
index 00000000000..9c17a3b0e4b
--- /dev/null
+++ b/spec/lib/gitlab/graphql/authorize_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Gitlab::Graphql::Authorize do
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ extend Gitlab::Graphql::Authorize
+
+ authorize :base_authorization
+ end
+ sub_class = Class.new(base_class) do
+ authorize :sub_authorization
+ end
+
+ expect(base_class.required_permissions).to contain_exactly(:base_authorization)
+ expect(sub_class.required_permissions)
+ .to contain_exactly(:base_authorization, :sub_authorization)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
new file mode 100644
index 00000000000..8f427d71a32
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe 'Setting WIP status of a merge request' do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:input) { { wip: true } }
+
+ let(:mutation) do
+ variables = {
+ project_path: project.full_path,
+ iid: merge_request.iid
+ }
+ graphql_mutation(:merge_request_set_wip, variables.merge(input))
+ end
+
+ def mutation_response
+ graphql_mutation_response(:merge_request_set_wip)
+ end
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it 'returns an error if the user is not allowed to update the merge request' do
+ post_graphql_mutation(mutation, current_user: create(:user))
+
+ expect(graphql_errors).not_to be_empty
+ end
+
+ it 'marks the merge request as WIP' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('WIP:')
+ end
+
+ it 'does not do anything if the merge request was already marked `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).to start_with('wip:')
+ end
+
+ context 'when passing WIP false as input' do
+ let(:input) { { wip: false } }
+
+ it 'does not do anything if the merge reqeust was not marked wip' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with(/wip\:/)
+ end
+
+ it 'unmarks the merge request as `WIP`' do
+ merge_request.update!(title: 'wip: hello world')
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['mergeRequest']['title']).not_to start_with('/wip\:/')
+ end
+ end
+end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index b9322975b5a..75827df80dc 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -1,4 +1,6 @@
module GraphqlHelpers
+ MutationDefinition = Struct.new(:query, :variables)
+
# makes an underscored string look like a fieldname
# "merge_request" => "mergeRequest"
def self.fieldnamerize(underscored_field_name)
@@ -41,6 +43,37 @@ module GraphqlHelpers
QUERY
end
+ def graphql_mutation(name, input, fields = nil)
+ mutation_name = GraphqlHelpers.fieldnamerize(name)
+ input_variable_name = "$#{input_variable_name_for_mutation(name)}"
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ fields ||= all_graphql_fields_for(mutation_field.type)
+
+ query = <<~MUTATION
+ mutation(#{input_variable_name}: #{mutation_field.arguments['input'].type}) {
+ #{mutation_name}(input: #{input_variable_name}) {
+ #{fields}
+ }
+ }
+ MUTATION
+ variables = variables_for_mutation(name, input)
+
+ MutationDefinition.new(query, variables)
+ end
+
+ def variables_for_mutation(name, input)
+ graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h
+ { input_variable_name_for_mutation(name) => graphql_input }.to_json
+ end
+
+ def input_variable_name_for_mutation(mutation_name)
+ mutation_name = GraphqlHelpers.fieldnamerize(mutation_name)
+ mutation_field = GitlabSchema.mutation.fields[mutation_name]
+ input_type = field_type(mutation_field.arguments['input'])
+
+ GraphqlHelpers.fieldnamerize(input_type)
+ end
+
def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
@@ -73,8 +106,12 @@ module GraphqlHelpers
end.join(", ")
end
- def post_graphql(query, current_user: nil)
- post api('/', current_user, version: 'graphql'), query: query
+ def post_graphql(query, current_user: nil, variables: nil)
+ post api('/', current_user, version: 'graphql'), query: query, variables: variables
+ end
+
+ def post_graphql_mutation(mutation, current_user: nil)
+ post_graphql(mutation.query, current_user: current_user, variables: mutation.variables)
end
def graphql_data
@@ -82,7 +119,11 @@ module GraphqlHelpers
end
def graphql_errors
- json_response['data']
+ json_response['errors']
+ end
+
+ def graphql_mutation_response(mutation_name)
+ graphql_data[GraphqlHelpers.fieldnamerize(mutation_name)]
end
def nested_fields?(field)
@@ -102,10 +143,14 @@ module GraphqlHelpers
end
def field_type(field)
- if field.type.respond_to?(:of_type)
- field.type.of_type
- else
- field.type
- end
+ field_type = field.type
+
+ # The type could be nested. For example `[GraphQL::STRING_TYPE]`:
+ # - List
+ # - String!
+ # - String
+ field_type = field_type.of_type while field_type.respond_to?(:of_type)
+
+ field_type
end
end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index be6fa4c71a0..7be84838e00 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -34,6 +34,15 @@ RSpec::Matchers.define :have_graphql_field do |field_name|
end
end
+RSpec::Matchers.define :have_graphql_mutation do |mutation_class|
+ match do |mutation_type|
+ field = mutation_type.fields[GraphqlHelpers.fieldnamerize(mutation_class.graphql_name)]
+
+ expect(field).to be_present
+ expect(field.resolver).to eq(mutation_class)
+ end
+end
+
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index fe7b7bc306f..04140cad3f0 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -5,7 +5,7 @@ shared_examples 'a working graphql query' do
it 'returns a successful response', :aggregate_failures do
expect(response).to have_gitlab_http_status(:success)
- expect(graphql_errors['errors']).to be_nil
+ expect(graphql_errors).to be_nil
expect(json_response.keys).to include('data')
end
end