summaryrefslogtreecommitdiff
path: root/spec/graphql/types
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 23:50:22 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-20 23:50:22 +0000
commit9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch)
tree70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/graphql/types
parent4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff)
downloadgitlab-ce-9dc93a4519d9d5d7be48ff274127136236a3adb3.tar.gz
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/graphql/types')
-rw-r--r--spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb3
-rw-r--r--spec/graphql/types/alert_management/prometheus_integration_type_spec.rb24
-rw-r--r--spec/graphql/types/base_enum_spec.rb48
-rw-r--r--spec/graphql/types/base_object_spec.rb432
-rw-r--r--spec/graphql/types/board_type_spec.rb14
-rw-r--r--spec/graphql/types/boards/board_issue_input_type_spec.rb4
-rw-r--r--spec/graphql/types/ci/job_status_enum_spec.rb13
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb28
-rw-r--r--spec/graphql/types/ci/pipeline_type_spec.rb5
-rw-r--r--spec/graphql/types/ci/recent_failures_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/stage_type_spec.rb1
-rw-r--r--spec/graphql/types/ci/test_case_status_enum_spec.rb13
-rw-r--r--spec/graphql/types/ci/test_case_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/test_report_summary_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/test_report_total_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/test_suite_summary_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/test_suite_type_spec.rb15
-rw-r--r--spec/graphql/types/global_id_type_spec.rb1
-rw-r--r--spec/graphql/types/issue_type_spec.rb2
-rw-r--r--spec/graphql/types/merge_request_review_state_enum_spec.rb18
-rw-r--r--spec/graphql/types/merge_requests/reviewer_type_spec.rb50
-rw-r--r--spec/graphql/types/milestone_type_spec.rb2
-rw-r--r--spec/graphql/types/packages/conan/file_metadatum_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb13
-rw-r--r--spec/graphql/types/packages/conan/metadatum_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/package_details_type_spec.rb (renamed from spec/graphql/types/packages/package_without_versions_type_spec.rb)4
-rw-r--r--spec/graphql/types/packages/package_file_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/package_type_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb32
-rw-r--r--spec/graphql/types/query_type_spec.rb2
-rw-r--r--spec/graphql/types/repository/blob_type_spec.rb9
-rw-r--r--spec/graphql/types/repository_type_spec.rb4
-rw-r--r--spec/graphql/types/snippet_type_spec.rb2
-rw-r--r--spec/graphql/types/timelog_type_spec.rb35
-rw-r--r--spec/graphql/types/user_merge_request_interaction_type_spec.rb116
35 files changed, 966 insertions, 40 deletions
diff --git a/spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb b/spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb
index c50092d7f0e..d1c2b4044c1 100644
--- a/spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb
+++ b/spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb
@@ -11,6 +11,7 @@ RSpec.describe GitlabSchema.types['UsageTrendsMeasurement'] do
describe 'authorization' do
let_it_be(:measurement) { create(:usage_trends_measurement, :project_count) }
+
let(:user) { create(:user) }
let(:query) do
@@ -44,7 +45,7 @@ RSpec.describe GitlabSchema.types['UsageTrendsMeasurement'] do
let(:user) { create(:user, :admin) }
before do
- stub_feature_flags(user_mode_in_session: false)
+ stub_application_setting(admin_mode: false)
end
it 'returns data' do
diff --git a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
index b10c2a2ab2a..d057afb331c 100644
--- a/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
+++ b/spec/graphql/types/alert_management/prometheus_integration_type_spec.rb
@@ -48,15 +48,21 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do
end
end
- context 'without project' do
- let_it_be(:integration) { create(:prometheus_service, project: nil, group: create(:group)) }
-
- it_behaves_like 'has field with value', 'token' do
- let(:value) { nil }
- end
-
- it_behaves_like 'has field with value', 'url' do
- let(:value) { nil }
+ describe 'a group integration' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:integration) { create(:prometheus_service, project: nil, group: group) }
+
+ # Since it is impossible to authorize the parent here, given that the
+ # project is nil, all fields should be redacted:
+
+ described_class.fields.each_key do |field_name|
+ context "field: #{field_name}" do
+ it 'is redacted' do
+ expect do
+ resolve_field(field_name, integration, current_user: user)
+ end.to raise_error(GraphqlHelpers::UnauthorizedObject)
+ end
+ end
end
end
end
diff --git a/spec/graphql/types/base_enum_spec.rb b/spec/graphql/types/base_enum_spec.rb
index 744aee40044..bab0278ee25 100644
--- a/spec/graphql/types/base_enum_spec.rb
+++ b/spec/graphql/types/base_enum_spec.rb
@@ -3,6 +3,38 @@
require 'spec_helper'
RSpec.describe Types::BaseEnum do
+ describe '.from_rails_enum' do
+ let(:enum_type) { Class.new(described_class) }
+ let(:template) { "The name is '%{name}', James %{name}." }
+
+ let(:enum) do
+ {
+ 'foo' => 1,
+ 'bar' => 2,
+ 'baz' => 100
+ }
+ end
+
+ it 'contructs the correct values' do
+ enum_type.from_rails_enum(enum, description: template)
+
+ expect(enum_type.values).to match(
+ 'FOO' => have_attributes(
+ description: "The name is 'foo', James foo.",
+ value: 'foo'
+ ),
+ 'BAR' => have_attributes(
+ description: "The name is 'bar', James bar.",
+ value: 'bar'
+ ),
+ 'BAZ' => have_attributes(
+ description: "The name is 'baz', James baz.",
+ value: 'baz'
+ )
+ )
+ end
+ end
+
describe '.declarative_enum' do
let(:use_name) { true }
let(:use_description) { true }
@@ -26,12 +58,15 @@ RSpec.describe Types::BaseEnum do
end
end
- subject(:set_declarative_enum) { enum_type.declarative_enum(enum_module, use_name: use_name, use_description: use_description) }
+ subject(:set_declarative_enum) do
+ enum_type.declarative_enum(enum_module, use_name: use_name, use_description: use_description)
+ end
describe '#graphql_name' do
context 'when the use_name is `true`' do
it 'changes the graphql_name' do
- expect { set_declarative_enum }.to change { enum_type.graphql_name }.from('OriginalName').to('Name')
+ expect { set_declarative_enum }
+ .to change(enum_type, :graphql_name).from('OriginalName').to('Name')
end
end
@@ -39,7 +74,8 @@ RSpec.describe Types::BaseEnum do
let(:use_name) { false }
it 'does not change the graphql_name' do
- expect { set_declarative_enum }.not_to change { enum_type.graphql_name }.from('OriginalName')
+ expect { set_declarative_enum }
+ .not_to change(enum_type, :graphql_name).from('OriginalName')
end
end
end
@@ -47,7 +83,8 @@ RSpec.describe Types::BaseEnum do
describe '#description' do
context 'when the use_description is `true`' do
it 'changes the description' do
- expect { set_declarative_enum }.to change { enum_type.description }.from('Original description').to('Description')
+ expect { set_declarative_enum }
+ .to change(enum_type, :description).from('Original description').to('Description')
end
end
@@ -55,7 +92,8 @@ RSpec.describe Types::BaseEnum do
let(:use_description) { false }
it 'does not change the description' do
- expect { set_declarative_enum }.not_to change { enum_type.description }.from('Original description')
+ expect { set_declarative_enum }
+ .not_to change(enum_type, :description).from('Original description')
end
end
end
diff --git a/spec/graphql/types/base_object_spec.rb b/spec/graphql/types/base_object_spec.rb
new file mode 100644
index 00000000000..d8f2ef58ea5
--- /dev/null
+++ b/spec/graphql/types/base_object_spec.rb
@@ -0,0 +1,432 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::BaseObject do
+ include GraphqlHelpers
+
+ describe 'scoping items' do
+ let_it_be(:custom_auth) do
+ Class.new(::Gitlab::Graphql::Authorize::ObjectAuthorization) do
+ def any?
+ true
+ end
+
+ def ok?(object, _current_user)
+ return false if object == { id: 100 }
+ return false if object.try(:deactivated?)
+
+ true
+ end
+ end
+ end
+
+ let_it_be(:test_schema) do
+ auth = custom_auth.new(nil)
+
+ base_object = Class.new(described_class) do
+ # Override authorization so we don't need to mock Ability
+ define_singleton_method :authorization do
+ auth
+ end
+ end
+
+ y_type = Class.new(base_object) do
+ graphql_name 'Y'
+ authorize :read_y
+ field :id, Integer, null: false
+
+ def id
+ object[:id]
+ end
+ end
+
+ number_type = Module.new do
+ include ::Types::BaseInterface
+
+ graphql_name 'Number'
+
+ field :value, Integer, null: false
+ end
+
+ odd_type = Class.new(described_class) do
+ graphql_name 'Odd'
+ implements number_type
+
+ authorize :read_odd
+ field :odd_value, Integer, null: false
+
+ def odd_value
+ object[:value]
+ end
+ end
+
+ even_type = Class.new(described_class) do
+ graphql_name 'Even'
+ implements number_type
+
+ authorize :read_even
+ field :even_value, Integer, null: false
+
+ def even_value
+ object[:value]
+ end
+ end
+
+ # an abstract type, delegating authorization to members
+ odd_or_even = Class.new(::Types::BaseUnion) do
+ graphql_name 'OddOrEven'
+
+ possible_types odd_type, even_type
+
+ define_singleton_method :resolve_type do |object, ctx|
+ if object[:value].odd?
+ odd_type
+ else
+ even_type
+ end
+ end
+ end
+
+ number_type.define_singleton_method :resolve_type do |object, ctx|
+ odd_or_even.resolve_type(object, ctx)
+ end
+
+ x_type = Class.new(base_object) do
+ graphql_name 'X'
+ # Scalar types
+ field :title, String, null: true
+ # monomorphic types
+ field :lazy_list_of_ys, [y_type], null: true
+ field :list_of_lazy_ys, [y_type], null: true
+ field :array_ys_conn, y_type.connection_type, null: true
+ # polymorphic types
+ field :polymorphic_conn, odd_or_even.connection_type, null: true
+ field :polymorphic_object, odd_or_even, null: true do
+ argument :value, Integer, required: true
+ end
+ field :interface_conn, number_type.connection_type, null: true
+
+ def lazy_list_of_ys
+ ::Gitlab::Graphql::Lazy.new { object[:ys] }
+ end
+
+ def list_of_lazy_ys
+ object[:ys].map { |y| ::Gitlab::Graphql::Lazy.new { y } }
+ end
+
+ def array_ys_conn
+ object[:ys].dup
+ end
+
+ def polymorphic_conn
+ object[:values].dup
+ end
+ alias_method :interface_conn, :polymorphic_conn
+
+ def polymorphic_object(value)
+ value
+ end
+ end
+
+ user_type = Class.new(base_object) do
+ graphql_name 'User'
+ authorize :read_user
+ field 'name', String, null: true
+ end
+
+ Class.new(GraphQL::Schema) do
+ lazy_resolve ::Gitlab::Graphql::Lazy, :force
+ use ::GraphQL::Pagination::Connections
+ use ::Gitlab::Graphql::Pagination::Connections
+
+ query(Class.new(::Types::BaseObject) do
+ graphql_name 'Query'
+ field :x, x_type, null: true
+ field :users, user_type.connection_type, null: true
+
+ def x
+ ::Gitlab::Graphql::Lazy.new { context[:x] }
+ end
+
+ def users
+ ::Gitlab::Graphql::Lazy.new { User.id_in(context[:user_ids]).order(id: :asc) }
+ end
+ end)
+
+ def unauthorized_object(err)
+ nil
+ end
+ end
+ end
+
+ def document(path)
+ GraphQL.parse(<<~GQL)
+ query {
+ x {
+ title
+ #{query_graphql_path(path, 'id')}
+ }
+ }
+ GQL
+ end
+
+ let(:data) do
+ {
+ x: {
+ title: 'Hey',
+ ys: [{ id: 1 }, { id: 100 }, { id: 2 }]
+ }
+ }
+ end
+
+ shared_examples 'array member redaction' do |path|
+ let(:result) do
+ query = GraphQL::Query.new(test_schema, document: document(path), context: data)
+ query.result.to_h
+ end
+
+ it 'redacts the unauthorized array member' do
+ expect(graphql_dig_at(result, 'data', 'x', 'title')).to eq('Hey')
+ expect(graphql_dig_at(result, 'data', 'x', *path)).to contain_exactly(
+ eq({ 'id' => 1 }),
+ eq({ 'id' => 2 })
+ )
+ end
+ end
+
+ # For example a batchloaded association
+ describe 'a lazy list' do
+ it_behaves_like 'array member redaction', %w[lazyListOfYs]
+ end
+
+ # For example using a batchloader to map over a set of IDs
+ describe 'a list of lazy items' do
+ it_behaves_like 'array member redaction', %w[listOfLazyYs]
+ end
+
+ describe 'an array connection of items' do
+ it_behaves_like 'array member redaction', %w[arrayYsConn nodes]
+ end
+
+ describe 'an array connection of items, selecting edges' do
+ it_behaves_like 'array member redaction', %w[arrayYsConn edges node]
+ end
+
+ it 'paginates arrays correctly' do
+ n = 7
+
+ data = {
+ x: {
+ ys: (95..105).to_a.map { |id| { id: id } }
+ }
+ }
+
+ doc = lambda do |after|
+ GraphQL.parse(<<~GQL)
+ query {
+ x {
+ ys: arrayYsConn(#{attributes_to_graphql(first: n, after: after)}) {
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ endCursor
+ }
+ nodes { id }
+ }
+ }
+ }
+ GQL
+ end
+ returned_items = ->(ids) { ids.to_a.map { |id| eq({ 'id' => id }) } }
+
+ query = GraphQL::Query.new(test_schema, document: doc[nil], context: data)
+ result = query.result.to_h
+
+ ys = result.dig('data', 'x', 'ys', 'nodes')
+ page = result.dig('data', 'x', 'ys', 'pageInfo')
+ # We expect this page to be smaller, since we paginate before redaction
+ expect(ys).to match_array(returned_items[(95..101).to_a - [100]])
+ expect(page).to include('hasNextPage' => true, 'hasPreviousPage' => false)
+
+ cursor = page['endCursor']
+ query_2 = GraphQL::Query.new(test_schema, document: doc[cursor], context: data)
+ result_2 = query_2.result.to_h
+
+ ys = result_2.dig('data', 'x', 'ys', 'nodes')
+ page = result_2.dig('data', 'x', 'ys', 'pageInfo')
+ expect(ys).to match_array(returned_items[102..105])
+ expect(page).to include('hasNextPage' => false, 'hasPreviousPage' => true)
+ end
+
+ it 'filters connections correctly' do
+ active_users = create_list(:user, 3, state: :active)
+ inactive = create(:user, state: :deactivated)
+
+ data = { user_ids: [inactive, *active_users].map(&:id) }
+
+ doc = GraphQL.parse(<<~GQL)
+ query {
+ users { nodes { name } }
+ }
+ GQL
+
+ query = GraphQL::Query.new(test_schema, document: doc, context: data)
+ result = query.result.to_h
+
+ expect(result.dig('data', 'users', 'nodes')).to match_array(active_users.map do |u|
+ eq({ 'name' => u.name })
+ end)
+ end
+
+ it 'filters polymorphic connections' do
+ data = {
+ current_user: :the_user,
+ x: {
+ values: [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
+ }
+ }
+
+ doc = GraphQL.parse(<<~GQL)
+ query {
+ x {
+ things: polymorphicConn {
+ nodes {
+ ... on Odd { oddValue }
+ ... on Even { evenValue }
+ }
+ }
+ }
+ }
+ GQL
+
+ # Each ability check happens twice: once in the collection, and once
+ # on the type. We expect the ability checks to be cached.
+ expect(Ability).to receive(:allowed?).twice
+ .with(:the_user, :read_odd, { value: 1 }).and_return(true)
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_odd, { value: 3 }).and_return(false)
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_even, { value: 2 }).and_return(false)
+ expect(Ability).to receive(:allowed?).twice
+ .with(:the_user, :read_even, { value: 4 }).and_return(true)
+
+ query = GraphQL::Query.new(test_schema, document: doc, context: data)
+ result = query.result.to_h
+
+ things = result.dig('data', 'x', 'things', 'nodes')
+
+ expect(things).to contain_exactly(
+ { 'oddValue' => 1 },
+ { 'evenValue' => 4 }
+ )
+ end
+
+ it 'filters interface connections' do
+ data = {
+ current_user: :the_user,
+ x: {
+ values: [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]
+ }
+ }
+
+ doc = GraphQL.parse(<<~GQL)
+ query {
+ x {
+ things: interfaceConn {
+ nodes {
+ value
+ ... on Odd { oddValue }
+ ... on Even { evenValue }
+ }
+ }
+ }
+ }
+ GQL
+
+ # Each ability check happens twice: once in the collection, and once
+ # on the type. We expect the ability checks to be cached.
+ expect(Ability).to receive(:allowed?).twice
+ .with(:the_user, :read_odd, { value: 1 }).and_return(true)
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_odd, { value: 3 }).and_return(false)
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_even, { value: 2 }).and_return(false)
+ expect(Ability).to receive(:allowed?).twice
+ .with(:the_user, :read_even, { value: 4 }).and_return(true)
+
+ query = GraphQL::Query.new(test_schema, document: doc, context: data)
+ result = query.result.to_h
+
+ things = result.dig('data', 'x', 'things', 'nodes')
+
+ expect(things).to contain_exactly(
+ { 'value' => 1, 'oddValue' => 1 },
+ { 'value' => 4, 'evenValue' => 4 }
+ )
+ end
+
+ it 'redacts polymorphic objects' do
+ data = {
+ current_user: :the_user,
+ x: {
+ values: [{ value: 1 }]
+ }
+ }
+
+ doc = GraphQL.parse(<<~GQL)
+ query {
+ x {
+ ok: polymorphicObject(value: 1) {
+ ... on Odd { oddValue }
+ ... on Even { evenValue }
+ }
+ bad: polymorphicObject(value: 3) {
+ ... on Odd { oddValue }
+ ... on Even { evenValue }
+ }
+ }
+ }
+ GQL
+
+ # Each ability check happens twice: once in the collection, and once
+ # on the type. We expect the ability checks to be cached.
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_odd, { value: 1 }).and_return(true)
+ expect(Ability).to receive(:allowed?).once
+ .with(:the_user, :read_odd, { value: 3 }).and_return(false)
+
+ query = GraphQL::Query.new(test_schema, document: doc, context: data)
+ result = query.result.to_h
+
+ expect(result.dig('data', 'x', 'ok')).to eq({ 'oddValue' => 1 })
+ expect(result.dig('data', 'x', 'bad')).to be_nil
+ end
+
+ it 'paginates before scoping' do
+ # Inactive first so they sort first
+ n = 3
+ inactive = create_list(:user, n - 1, state: :deactivated)
+ active_users = create_list(:user, 2, state: :active)
+
+ data = { user_ids: [*inactive, *active_users].map(&:id) }
+
+ doc = GraphQL.parse(<<~GQL)
+ query {
+ users(first: #{n}) {
+ pageInfo { hasNextPage }
+ nodes { name } }
+ }
+ GQL
+
+ query = GraphQL::Query.new(test_schema, document: doc, context: data)
+ result = query.result.to_h
+
+ # We expect the page to be loaded and then filtered - i.e. to have all
+ # deactivated users removed.
+ expect(result.dig('data', 'users', 'pageInfo', 'hasNextPage')).to be_truthy
+ expect(result.dig('data', 'users', 'nodes'))
+ .to contain_exactly({ 'name' => active_users.first.name })
+ end
+ end
+end
diff --git a/spec/graphql/types/board_type_spec.rb b/spec/graphql/types/board_type_spec.rb
index dca3cfd8aaf..403fbe1f290 100644
--- a/spec/graphql/types/board_type_spec.rb
+++ b/spec/graphql/types/board_type_spec.rb
@@ -8,8 +8,18 @@ RSpec.describe GitlabSchema.types['Board'] do
specify { expect(described_class).to require_graphql_authorizations(:read_issue_board) }
it 'has specific fields' do
- expected_fields = %w[id name web_url web_path]
+ expected_fields = %w[
+ id
+ name
+ hideBacklogList
+ hideClosedList
+ createdAt
+ updatedAt
+ lists
+ webPath
+ webUrl
+ ]
- expect(described_class).to include_graphql_fields(*expected_fields)
+ expect(described_class).to have_graphql_fields(*expected_fields).at_least
end
end
diff --git a/spec/graphql/types/boards/board_issue_input_type_spec.rb b/spec/graphql/types/boards/board_issue_input_type_spec.rb
index 6319ff9a88e..5d3efb9b40d 100644
--- a/spec/graphql/types/boards/board_issue_input_type_spec.rb
+++ b/spec/graphql/types/boards/board_issue_input_type_spec.rb
@@ -5,9 +5,9 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['BoardIssueInput'] do
it { expect(described_class.graphql_name).to eq('BoardIssueInput') }
- it 'exposes negated issue arguments' do
+ it 'has specific fields' do
allowed_args = %w(labelName milestoneTitle assigneeUsername authorUsername
- releaseTag myReactionEmoji not search)
+ releaseTag myReactionEmoji not search assigneeWildcardId)
expect(described_class.arguments.keys).to include(*allowed_args)
expect(described_class.arguments['not'].type).to eq(Types::Boards::NegatedBoardIssueInputType)
diff --git a/spec/graphql/types/ci/job_status_enum_spec.rb b/spec/graphql/types/ci/job_status_enum_spec.rb
new file mode 100644
index 00000000000..e8a1a2e0aa8
--- /dev/null
+++ b/spec/graphql/types/ci/job_status_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiJobStatus'] do
+ it 'exposes all job status values' do
+ expect(described_class.values.values).to contain_exactly(
+ *::Ci::HasStatus::AVAILABLE_STATUSES.map do |status|
+ have_attributes(value: status, graphql_name: status.upcase)
+ end
+ )
+ end
+end
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index 25f626cea0f..787e2174070 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -8,14 +8,32 @@ RSpec.describe Types::Ci::JobType do
it 'exposes the expected fields' do
expected_fields = %i[
- pipeline
+ active
+ allow_failure
+ artifacts
+ cancelable
+ commitPath
+ coverage
+ created_at
+ detailedStatus
+ duration
+ finished_at
+ id
name
needs
- detailedStatus
+ pipeline
+ playable
+ queued_at
+ refName
+ refPath
+ retryable
scheduledAt
- artifacts
- finished_at
- duration
+ schedulingType
+ shortSha
+ stage
+ started_at
+ status
+ tags
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb
index e0e84a1b635..c7d2cbdb765 100644
--- a/spec/graphql/types/ci/pipeline_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_type_spec.rb
@@ -11,8 +11,9 @@ RSpec.describe Types::Ci::PipelineType do
expected_fields = %w[
id iid sha before_sha status detailed_status config_source duration
coverage created_at updated_at started_at finished_at committed_at
- stages user retryable cancelable jobs source_job downstream
- upstream path project active user_permissions warnings commit_path
+ stages user retryable cancelable jobs source_job job downstream
+ upstream path project active user_permissions warnings commit_path uses_needs
+ test_report_summary test_suite
]
if Gitlab.ee?
diff --git a/spec/graphql/types/ci/recent_failures_type_spec.rb b/spec/graphql/types/ci/recent_failures_type_spec.rb
new file mode 100644
index 00000000000..38369da46bf
--- /dev/null
+++ b/spec/graphql/types/ci/recent_failures_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::RecentFailuresType do
+ specify { expect(described_class.graphql_name).to eq('RecentFailures') }
+
+ it 'contains attributes related to a recent failure history for a test case' do
+ expected_fields = %w[
+ count base_branch
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/stage_type_spec.rb b/spec/graphql/types/ci/stage_type_spec.rb
index 9a8d4fa96a3..cb8c1cb02cd 100644
--- a/spec/graphql/types/ci/stage_type_spec.rb
+++ b/spec/graphql/types/ci/stage_type_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe Types::Ci::StageType do
name
groups
detailedStatus
+ jobs
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/test_case_status_enum_spec.rb b/spec/graphql/types/ci/test_case_status_enum_spec.rb
new file mode 100644
index 00000000000..ba2d1aefb20
--- /dev/null
+++ b/spec/graphql/types/ci/test_case_status_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestCaseStatusEnum do
+ specify { expect(described_class.graphql_name).to eq('TestCaseStatus') }
+
+ it 'exposes all test case status types' do
+ expect(described_class.values.keys).to eq(
+ ::Gitlab::Ci::Reports::TestCase::STATUS_TYPES
+ )
+ end
+end
diff --git a/spec/graphql/types/ci/test_case_type_spec.rb b/spec/graphql/types/ci/test_case_type_spec.rb
new file mode 100644
index 00000000000..e6cd70c287e
--- /dev/null
+++ b/spec/graphql/types/ci/test_case_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestCaseType do
+ specify { expect(described_class.graphql_name).to eq('TestCase') }
+
+ it 'contains attributes related to a pipeline test case' do
+ expected_fields = %w[
+ name status classname file attachment_url execution_time stack_trace system_output recent_failures
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/test_report_summary_type_spec.rb b/spec/graphql/types/ci/test_report_summary_type_spec.rb
new file mode 100644
index 00000000000..06974da0b88
--- /dev/null
+++ b/spec/graphql/types/ci/test_report_summary_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestReportSummaryType do
+ specify { expect(described_class.graphql_name).to eq('TestReportSummary') }
+
+ it 'contains attributes related to a pipeline test report summary' do
+ expected_fields = %w[
+ total test_suites
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/test_report_total_type_spec.rb b/spec/graphql/types/ci/test_report_total_type_spec.rb
new file mode 100644
index 00000000000..e5b7b358edb
--- /dev/null
+++ b/spec/graphql/types/ci/test_report_total_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestReportTotalType do
+ specify { expect(described_class.graphql_name).to eq('TestReportTotal') }
+
+ it 'contains attributes related to a pipeline test report summary' do
+ expected_fields = %w[
+ time count success failed skipped error suite_error
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/test_suite_summary_type_spec.rb b/spec/graphql/types/ci/test_suite_summary_type_spec.rb
new file mode 100644
index 00000000000..e87782037c7
--- /dev/null
+++ b/spec/graphql/types/ci/test_suite_summary_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestSuiteSummaryType do
+ specify { expect(described_class.graphql_name).to eq('TestSuiteSummary') }
+
+ it 'contains attributes related to a pipeline test report summary' do
+ expected_fields = %w[
+ name total_time total_count success_count failed_count skipped_count error_count suite_error build_ids
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/test_suite_type_spec.rb b/spec/graphql/types/ci/test_suite_type_spec.rb
new file mode 100644
index 00000000000..d9caca3e2c3
--- /dev/null
+++ b/spec/graphql/types/ci/test_suite_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Ci::TestSuiteType do
+ specify { expect(described_class.graphql_name).to eq('TestSuite') }
+
+ it 'contains attributes related to a pipeline test suite' do
+ expected_fields = %w[
+ name total_time total_count success_count failed_count skipped_count error_count suite_error test_cases
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb
index 8eb023ad2a3..4df51dc8d1b 100644
--- a/spec/graphql/types/global_id_type_spec.rb
+++ b/spec/graphql/types/global_id_type_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Types::GlobalIDType do
let_it_be(:project) { create(:project) }
+
let(:gid) { project.to_global_id }
it 'is has the correct name' do
diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb
index 21fc530149c..6908a610aae 100644
--- a/spec/graphql/types/issue_type_spec.rb
+++ b/spec/graphql/types/issue_type_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
confidential discussion_locked upvotes downvotes user_notes_count user_discussions_count web_path web_url relative_position
emails_disabled subscribed time_estimate total_time_spent human_time_estimate human_total_time_spent closed_at created_at updated_at task_completion_status
design_collection alert_management_alert severity current_user_todos moved moved_to
- create_note_email]
+ create_note_email timelogs]
fields.each do |field_name|
expect(described_class).to have_graphql_field(field_name)
diff --git a/spec/graphql/types/merge_request_review_state_enum_spec.rb b/spec/graphql/types/merge_request_review_state_enum_spec.rb
new file mode 100644
index 00000000000..486e1c4f502
--- /dev/null
+++ b/spec/graphql/types/merge_request_review_state_enum_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
+ it 'the correct enum members' do
+ expect(described_class.values).to match(
+ 'REVIEWED' => have_attributes(
+ description: 'The merge request is reviewed.',
+ value: 'reviewed'
+ ),
+ 'UNREVIEWED' => have_attributes(
+ description: 'The merge request is unreviewed.',
+ value: 'unreviewed'
+ )
+ )
+ end
+end
diff --git a/spec/graphql/types/merge_requests/reviewer_type_spec.rb b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
new file mode 100644
index 00000000000..c2182e9968c
--- /dev/null
+++ b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MergeRequestReviewer'] do
+ specify { expect(described_class).to require_graphql_authorizations(:read_user) }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ id
+ bot
+ user_permissions
+ snippets
+ name
+ username
+ email
+ publicEmail
+ avatarUrl
+ webUrl
+ webPath
+ todos
+ state
+ status
+ location
+ authoredMergeRequests
+ assignedMergeRequests
+ reviewRequestedMergeRequests
+ groupMemberships
+ groupCount
+ projectMemberships
+ starredProjects
+ callouts
+ merge_request_interaction
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+
+ describe '#merge_request_interaction' do
+ subject { described_class.fields['mergeRequestInteraction'] }
+
+ it 'returns the correct type' do
+ is_expected.to have_graphql_type(Types::UserMergeRequestInteractionType)
+ end
+
+ it 'has the correct arguments' do
+ is_expected.to have_attributes(arguments: be_empty)
+ end
+ end
+end
diff --git a/spec/graphql/types/milestone_type_spec.rb b/spec/graphql/types/milestone_type_spec.rb
index 806495250ac..5c2ae5cea3c 100644
--- a/spec/graphql/types/milestone_type_spec.rb
+++ b/spec/graphql/types/milestone_type_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do
it 'has the expected fields' do
expected_fields = %w[
- id title description state web_path
+ id iid title description state web_path
due_date start_date created_at updated_at
project_milestone group_milestone subgroup_milestone
stats
diff --git a/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb b/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb
new file mode 100644
index 00000000000..18b17286654
--- /dev/null
+++ b/spec/graphql/types/packages/conan/file_metadatum_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ConanFileMetadata'] do
+ it 'includes conan file metadatum fields' do
+ expected_fields = %w[
+ id created_at updated_at recipe_revision package_revision conan_package_reference conan_file_type
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb b/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb
new file mode 100644
index 00000000000..379cb5168a8
--- /dev/null
+++ b/spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ConanMetadatumFileTypeEnum'] do
+ it 'uses all possible options from model' do
+ expected_keys = ::Packages::Conan::FileMetadatum.conan_file_types
+ .keys
+ .map(&:upcase)
+
+ expect(described_class.values.keys).to contain_exactly(*expected_keys)
+ end
+end
diff --git a/spec/graphql/types/packages/conan/metadatum_type_spec.rb b/spec/graphql/types/packages/conan/metadatum_type_spec.rb
new file mode 100644
index 00000000000..f8f24ffc95a
--- /dev/null
+++ b/spec/graphql/types/packages/conan/metadatum_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['ConanMetadata'] do
+ it 'includes conan metadatum fields' do
+ expected_fields = %w[
+ id created_at updated_at package_username package_channel recipe recipe_path
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/package_without_versions_type_spec.rb b/spec/graphql/types/packages/package_details_type_spec.rb
index faa79e588d5..06093813315 100644
--- a/spec/graphql/types/packages/package_without_versions_type_spec.rb
+++ b/spec/graphql/types/packages/package_details_type_spec.rb
@@ -2,10 +2,10 @@
require 'spec_helper'
-RSpec.describe GitlabSchema.types['PackageWithoutVersions'] do
+RSpec.describe GitlabSchema.types['PackageDetailsType'] do
it 'includes all the package fields' do
expected_fields = %w[
- id name version created_at updated_at package_type tags project pipelines
+ id name version created_at updated_at package_type tags project pipelines versions package_files
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/packages/package_file_type_spec.rb b/spec/graphql/types/packages/package_file_type_spec.rb
new file mode 100644
index 00000000000..8e20aea5220
--- /dev/null
+++ b/spec/graphql/types/packages/package_file_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PackageFile'] do
+ it 'includes package file fields' do
+ expected_fields = %w[
+ id file_name created_at updated_at size file_name download_path file_md5 file_sha1 file_sha256 file_metadata
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/package_type_spec.rb b/spec/graphql/types/packages/package_type_spec.rb
index 43289a019b3..544d6ddc3af 100644
--- a/spec/graphql/types/packages/package_type_spec.rb
+++ b/spec/graphql/types/packages/package_type_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Package'] do
id name version package_type
created_at updated_at
project
- tags pipelines versions
+ tags pipelines metadata versions
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 9579ef8b99b..f2c4068f048 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -106,7 +106,8 @@ RSpec.describe GitlabSchema.types['Project'] do
expect(secure_analyzers_prefix['type']).to eq('string')
expect(secure_analyzers_prefix['field']).to eq('SECURE_ANALYZERS_PREFIX')
expect(secure_analyzers_prefix['label']).to eq('Image prefix')
- expect(secure_analyzers_prefix['defaultValue']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
+ expect(secure_analyzers_prefix['defaultValue'])
+ .to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
expect(secure_analyzers_prefix['value']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
expect(secure_analyzers_prefix['size']).to eq('LARGE')
expect(secure_analyzers_prefix['options']).to be_nil
@@ -124,8 +125,8 @@ RSpec.describe GitlabSchema.types['Project'] do
it "returns the project's sast configuration for analyzer variables" do
analyzer = subject.dig('data', 'project', 'sastCiConfiguration', 'analyzers', 'nodes').first
- expect(analyzer['name']).to eq('brakeman')
- expect(analyzer['label']).to eq('Brakeman')
+ expect(analyzer['name']).to eq('bandit')
+ expect(analyzer['label']).to eq('Bandit')
expect(analyzer['enabled']).to eq(true)
end
@@ -184,9 +185,11 @@ RSpec.describe GitlabSchema.types['Project'] do
context 'when repository is accessible only by team members' do
it "returns no configuration" do
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED,
- builds_access_level: ProjectFeature::DISABLED,
- repository_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ builds_access_level: ProjectFeature::DISABLED,
+ repository_access_level: ProjectFeature::PRIVATE
+ )
secure_analyzers_prefix = subject.dig('data', 'project', 'sastCiConfiguration')
expect(secure_analyzers_prefix).to be_nil
@@ -240,6 +243,7 @@ RSpec.describe GitlabSchema.types['Project'] do
:assignee_username,
:reviewer_username,
:milestone_title,
+ :not,
:sort
)
end
@@ -342,8 +346,13 @@ RSpec.describe GitlabSchema.types['Project'] do
let_it_be(:project) { create(:project, :public) }
context 'when project has Jira imports' do
- let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) }
- let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) }
+ let_it_be(:jira_import1) do
+ create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago)
+ end
+
+ let_it_be(:jira_import2) do
+ create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago)
+ end
it 'retrieves the imports' do
expect(subject).to contain_exactly(jira_import1, jira_import2)
@@ -363,4 +372,11 @@ RSpec.describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_type(Types::Ci::AnalyticsType) }
it { is_expected.to have_graphql_resolver(Resolvers::ProjectPipelineStatisticsResolver) }
end
+
+ describe 'jobs field' do
+ subject { described_class.fields['jobs'] }
+
+ it { is_expected.to have_graphql_type(Types::Ci::JobType.connection_type) }
+ it { is_expected.to have_graphql_arguments(:statuses) }
+ end
end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index cb8e875dbf4..d3dcdd260b0 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -98,6 +98,6 @@ RSpec.describe GitlabSchema.types['Query'] do
describe 'package field' do
subject { described_class.fields['package'] }
- it { is_expected.to have_graphql_type(Types::Packages::PackageType) }
+ it { is_expected.to have_graphql_type(Types::Packages::PackageDetailsType) }
end
end
diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb
new file mode 100644
index 00000000000..f8647e4e964
--- /dev/null
+++ b/spec/graphql/types/repository/blob_type_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::Repository::BlobType do
+ specify { expect(described_class.graphql_name).to eq('RepositoryBlob') }
+
+ specify { expect(described_class).to have_graphql_fields(:id, :oid, :name, :path, :web_path, :lfs_oid, :mode) }
+end
diff --git a/spec/graphql/types/repository_type_spec.rb b/spec/graphql/types/repository_type_spec.rb
index e9199bd286e..fa1e54dfcfa 100644
--- a/spec/graphql/types/repository_type_spec.rb
+++ b/spec/graphql/types/repository_type_spec.rb
@@ -12,4 +12,8 @@ RSpec.describe GitlabSchema.types['Repository'] do
specify { expect(described_class).to have_graphql_field(:tree) }
specify { expect(described_class).to have_graphql_field(:exists, calls_gitaly?: true, complexity: 2) }
+
+ specify { expect(described_class).to have_graphql_field(:blobs) }
+
+ specify { expect(described_class).to have_graphql_field(:branch_names, calls_gitaly?: true, complexity: 170) }
end
diff --git a/spec/graphql/types/snippet_type_spec.rb b/spec/graphql/types/snippet_type_spec.rb
index 4d827186a9b..b87770ebe8d 100644
--- a/spec/graphql/types/snippet_type_spec.rb
+++ b/spec/graphql/types/snippet_type_spec.rb
@@ -161,6 +161,7 @@ RSpec.describe GitlabSchema.types['Snippet'] do
describe '#blobs' do
let_it_be(:snippet) { create(:personal_snippet, :public, author: user) }
+
let(:query_blobs) { subject.dig('data', 'snippets', 'nodes')[0].dig('blobs', 'nodes') }
let(:paths) { [] }
let(:query) do
@@ -201,6 +202,7 @@ RSpec.describe GitlabSchema.types['Snippet'] do
context 'when snippet has repository' do
let_it_be(:snippet) { create(:personal_snippet, :repository, :public, author: user) }
+
let(:blobs) { snippet.blobs }
it_behaves_like 'an array'
diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb
new file mode 100644
index 00000000000..38bd70d5097
--- /dev/null
+++ b/spec/graphql/types/timelog_type_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['Timelog'] do
+ let(:fields) { %i[spent_at time_spent user issue note] }
+
+ it { expect(described_class.graphql_name).to eq('Timelog') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to require_graphql_authorizations(:read_group_timelogs) }
+
+ describe 'user field' do
+ subject { described_class.fields['user'] }
+
+ it 'returns user' do
+ is_expected.to have_non_null_graphql_type(Types::UserType)
+ end
+ end
+
+ describe 'issue field' do
+ subject { described_class.fields['issue'] }
+
+ it 'returns issue' do
+ is_expected.to have_graphql_type(Types::IssueType)
+ end
+ end
+
+ describe 'note field' do
+ subject { described_class.fields['note'] }
+
+ it 'returns note' do
+ is_expected.to have_graphql_type(Types::Notes::NoteType)
+ end
+ end
+end
diff --git a/spec/graphql/types/user_merge_request_interaction_type_spec.rb b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
new file mode 100644
index 00000000000..f424c9200ab
--- /dev/null
+++ b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+
+ let(:interaction) { ::Users::MergeRequestInteraction.new(user: user, merge_request: merge_request.reset) }
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_merge_request) }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ can_merge
+ can_update
+ review_state
+ reviewed
+ approved
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields).at_least
+ end
+
+ def resolve(field_name)
+ resolve_field(field_name, interaction, current_user: current_user)
+ end
+
+ describe '#can_merge' do
+ subject { resolve(:can_merge) }
+
+ context 'when the user cannot merge' do
+ it { is_expected.to be false }
+ end
+
+ context 'when the user can merge' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it { is_expected.to be true }
+ end
+ end
+
+ describe '#can_update' do
+ subject { resolve(:can_update) }
+
+ context 'when the user cannot update the MR' do
+ it { is_expected.to be false }
+ end
+
+ context 'when the user can update the MR' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.to be true }
+ end
+ end
+
+ describe '#review_state' do
+ subject { resolve(:review_state) }
+
+ context 'when the user has not been asked to review the MR' do
+ it { is_expected.to be_nil }
+
+ it 'implies not reviewed' do
+ expect(resolve(:reviewed)).to be false
+ end
+ end
+
+ context 'when the user has been asked to review the MR' do
+ before do
+ merge_request.reviewers << user
+ end
+
+ it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['UNREVIEWED'].value) }
+
+ it 'implies not reviewed' do
+ expect(resolve(:reviewed)).to be false
+ end
+ end
+
+ context 'when the user has provided a review' do
+ before do
+ merge_request.merge_request_reviewers.create!(reviewer: user, state: MergeRequestReviewer.states['reviewed'])
+ end
+
+ it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['REVIEWED'].value) }
+
+ it 'implies reviewed' do
+ expect(resolve(:reviewed)).to be true
+ end
+ end
+ end
+
+ describe '#approved' do
+ subject { resolve(:approved) }
+
+ context 'when the user has not approved the MR' do
+ it { is_expected.to be false }
+ end
+
+ context 'when the user has approved the MR' do
+ before do
+ merge_request.approved_by_users << user
+ end
+
+ it { is_expected.to be true }
+ end
+ end
+end