Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 12:08:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 12:08:42 +0300
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /spec/graphql
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'spec/graphql')
-rw-r--r--spec/graphql/features/authorization_spec.rb6
-rw-r--r--spec/graphql/gitlab_schema_spec.rb89
-rw-r--r--spec/graphql/mutations/base_mutation_spec.rb56
-rw-r--r--spec/graphql/mutations/ci/runner/delete_spec.rb2
-rw-r--r--spec/graphql/mutations/ci/runner/update_spec.rb2
-rw-r--r--spec/graphql/mutations/design_management/delete_spec.rb39
-rw-r--r--spec/graphql/mutations/groups/update_spec.rb74
-rw-r--r--spec/graphql/mutations/issues/update_spec.rb22
-rw-r--r--spec/graphql/resolvers/base_resolver_spec.rb38
-rw-r--r--spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/concerns/resolves_ids_spec.rb43
-rw-r--r--spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb2
-rw-r--r--spec/graphql/resolvers/echo_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/groups_resolver_spec.rb133
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb72
-rw-r--r--spec/graphql/resolvers/merge_requests_count_resolver_spec.rb45
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb23
-rw-r--r--spec/graphql/resolvers/namespace_projects_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/paginated_tree_resolver_spec.rb102
-rw-r--r--spec/graphql/resolvers/project_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/terraform/states_resolver_spec.rb3
-rw-r--r--spec/graphql/resolvers/timelog_resolver_spec.rb309
-rw-r--r--spec/graphql/resolvers/user_discussions_count_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/user_notes_count_resolver_spec.rb2
-rw-r--r--spec/graphql/subscriptions/issuable_updated_spec.rb2
-rw-r--r--spec/graphql/types/base_argument_spec.rb38
-rw-r--r--spec/graphql/types/base_field_spec.rb32
-rw-r--r--spec/graphql/types/global_id_type_spec.rb4
-rw-r--r--spec/graphql/types/group_type_spec.rb11
-rw-r--r--spec/graphql/types/issue_type_spec.rb2
-rw-r--r--spec/graphql/types/merge_requests/reviewer_type_spec.rb2
-rw-r--r--spec/graphql/types/namespace_type_spec.rb2
-rw-r--r--spec/graphql/types/packages/nuget/dependency_link_metdatum_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/package_dependency_link_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/package_dependency_type_enum_spec.rb9
-rw-r--r--spec/graphql/types/packages/package_dependency_type_spec.rb13
-rw-r--r--spec/graphql/types/packages/package_details_type_spec.rb2
-rw-r--r--spec/graphql/types/permission_types/base_permission_type_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb11
-rw-r--r--spec/graphql/types/query_type_spec.rb11
-rw-r--r--spec/graphql/types/range_input_type_spec.rb6
-rw-r--r--spec/graphql/types/repository_type_spec.rb2
-rw-r--r--spec/graphql/types/timelog_type_spec.rb2
-rw-r--r--spec/graphql/types/user_type_spec.rb11
46 files changed, 1063 insertions, 207 deletions
diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb
index 0dc3a9c85e7..faf19104731 100644
--- a/spec/graphql/features/authorization_spec.rb
+++ b/spec/graphql/features/authorization_spec.rb
@@ -105,7 +105,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do
describe 'with a single permission' do
let(:type) do
type_factory do |type|
- type.field :name, GraphQL::STRING_TYPE, null: true, authorize: permission_single
+ type.field :name, GraphQL::Types::String, null: true, authorize: permission_single
end
end
@@ -124,7 +124,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do
let(:type) do
permissions = permission_collection
type_factory do |type|
- type.field :name, GraphQL::STRING_TYPE,
+ type.field :name, GraphQL::Types::String,
null: true,
authorize: permissions
end
@@ -332,7 +332,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do
type_factory do |type|
type.graphql_name 'FakeIssueType'
type.authorize :read_issue
- type.field :id, GraphQL::ID_TYPE, null: false
+ type.field :id, GraphQL::Types::ID, null: false
end
end
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index 06505536b09..3fa0dc95126 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -36,75 +36,66 @@ RSpec.describe GitlabSchema do
end
describe '.execute' do
- context 'with different types of users' do
- context 'when no context' do
- it 'returns DEFAULT_MAX_COMPLEXITY' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
-
- described_class.execute('query')
+ describe 'setting query `max_complexity` and `max_depth`' do
+ subject(:result) { described_class.execute('query', **kwargs).query }
+
+ shared_examples 'sets default limits' do
+ specify do
+ expect(result).to have_attributes(
+ max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY,
+ max_depth: GitlabSchema::DEFAULT_MAX_DEPTH
+ )
end
end
- context 'when no user' do
- it 'returns DEFAULT_MAX_COMPLEXITY' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
+ context 'with no context' do
+ let(:kwargs) { {} }
- described_class.execute('query', context: {})
- end
-
- it 'returns DEFAULT_MAX_DEPTH' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_depth: GitlabSchema::DEFAULT_MAX_DEPTH))
-
- described_class.execute('query', context: {})
- end
+ include_examples 'sets default limits'
end
- context 'when a logged in user' do
- it 'returns AUTHENTICATED_COMPLEXITY' do
- expect(GraphQL::Schema).to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::AUTHENTICATED_COMPLEXITY))
+ context 'with no :current_user' do
+ let(:kwargs) { { context: {} } }
- described_class.execute('query', context: { current_user: user })
- end
+ include_examples 'sets default limits'
+ end
- it 'returns AUTHENTICATED_MAX_DEPTH' do
- expect(GraphQL::Schema).to receive(:execute)
- .with('query', hash_including(max_depth: GitlabSchema::AUTHENTICATED_MAX_DEPTH))
+ context 'with anonymous user' do
+ let(:kwargs) { { context: { current_user: nil } } }
- described_class.execute('query', context: { current_user: user })
- end
+ include_examples 'sets default limits'
end
- context 'when an admin user' do
- it 'returns ADMIN_COMPLEXITY' do
- user = build :user, :admin
-
- expect(GraphQL::Schema).to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::ADMIN_COMPLEXITY))
+ context 'with a logged in user' do
+ let(:kwargs) { { context: { current_user: user } } }
- described_class.execute('query', context: { current_user: user })
+ it 'sets authenticated user limits' do
+ expect(result).to have_attributes(
+ max_complexity: GitlabSchema::AUTHENTICATED_MAX_COMPLEXITY,
+ max_depth: GitlabSchema::AUTHENTICATED_MAX_DEPTH
+ )
end
end
- context 'when max_complexity passed on the query' do
- it 'returns what was passed on the query' do
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: 1234))
+ context 'with an admin user' do
+ let(:kwargs) { { context: { current_user: build(:user, :admin) } } }
- described_class.execute('query', max_complexity: 1234)
+ it 'sets admin/authenticated user limits' do
+ expect(result).to have_attributes(
+ max_complexity: GitlabSchema::ADMIN_MAX_COMPLEXITY,
+ max_depth: GitlabSchema::AUTHENTICATED_MAX_DEPTH
+ )
end
end
- context 'when max_depth passed on the query' do
- it 'returns what was passed on the query' do
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_depth: 1234))
+ context 'when limits passed as kwargs' do
+ let(:kwargs) { { max_complexity: 1234, max_depth: 4321 } }
- described_class.execute('query', max_depth: 1234)
+ it 'sets limits from the kwargs' do
+ expect(result).to have_attributes(
+ max_complexity: 1234,
+ max_depth: 4321
+ )
end
end
end
diff --git a/spec/graphql/mutations/base_mutation_spec.rb b/spec/graphql/mutations/base_mutation_spec.rb
new file mode 100644
index 00000000000..7939fadb37b
--- /dev/null
+++ b/spec/graphql/mutations/base_mutation_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Mutations::BaseMutation do
+ include GraphqlHelpers
+
+ describe 'argument nullability' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:context) { { current_user: user } }
+
+ subject(:mutation) { mutation_class.new(object: nil, context: context, field: nil) }
+
+ describe 'when using a mutation with correct argument declarations' do
+ context 'when argument is nullable and required' do
+ let(:mutation_class) do
+ Class.new(described_class) do
+ argument :foo, GraphQL::Types::String, required: :nullable
+ end
+ end
+
+ specify do
+ expect { subject.ready? }.to raise_error(ArgumentError, /must be provided: foo/)
+ end
+
+ specify do
+ expect { subject.ready?(foo: nil) }.not_to raise_error
+ end
+
+ specify do
+ expect { subject.ready?(foo: "bar") }.not_to raise_error
+ end
+ end
+
+ context 'when argument is required and NOT nullable' do
+ let(:mutation_class) do
+ Class.new(described_class) do
+ argument :foo, GraphQL::Types::String, required: true
+ end
+ end
+
+ specify do
+ expect { subject.ready? }.to raise_error(ArgumentError, /must be provided/)
+ end
+
+ specify do
+ expect { subject.ready?(foo: nil) }.to raise_error(ArgumentError, /must be provided/)
+ end
+
+ specify do
+ expect { subject.ready?(foo: "bar") }.not_to raise_error
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/mutations/ci/runner/delete_spec.rb b/spec/graphql/mutations/ci/runner/delete_spec.rb
index 82873c96c3e..27e8236d593 100644
--- a/spec/graphql/mutations/ci/runner/delete_spec.rb
+++ b/spec/graphql/mutations/ci/runner/delete_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Mutations::Ci::Runner::Delete do
let(:mutation_params) { {} }
it 'raises an error' do
- expect { subject }.to raise_error(ArgumentError, "missing keyword: :id")
+ expect { subject }.to raise_error(ArgumentError, "Arguments must be provided: id")
end
end
diff --git a/spec/graphql/mutations/ci/runner/update_spec.rb b/spec/graphql/mutations/ci/runner/update_spec.rb
index 3db0d552a05..83150c3d7f6 100644
--- a/spec/graphql/mutations/ci/runner/update_spec.rb
+++ b/spec/graphql/mutations/ci/runner/update_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Mutations::Ci::Runner::Update do
let(:mutation_params) { {} }
it 'raises an error' do
- expect { subject }.to raise_error(ArgumentError, "missing keyword: :id")
+ expect { subject }.to raise_error(ArgumentError, "Arguments must be provided: id")
end
end
diff --git a/spec/graphql/mutations/design_management/delete_spec.rb b/spec/graphql/mutations/design_management/delete_spec.rb
index 3efa865c64b..93fff5e5103 100644
--- a/spec/graphql/mutations/design_management/delete_spec.rb
+++ b/spec/graphql/mutations/design_management/delete_spec.rb
@@ -86,9 +86,9 @@ RSpec.describe Mutations::DesignManagement::Delete do
end
end
- it 'runs no more than 28 queries' do
+ it 'runs no more than 29 queries' do
filenames.each(&:present?) # ignore setup
- # Queries: as of 2019-08-28
+ # Queries: as of 2021-07-22
# -------------
# 01. routing query
# 02. find project by id
@@ -100,25 +100,26 @@ RSpec.describe Mutations::DesignManagement::Delete do
# 09. find namespace by id
# 10. find group namespace by id
# 11. project.authorizations for user (same query as 5)
- # 12. project.project_features (same query as 3)
- # 13. project.authorizations for user (same query as 5)
- # 14. current designs by filename and issue
- # 15, 16 project.authorizations for user (same query as 5)
- # 17. find route by id and source_type
+ # 12. find user by id
+ # 13. project.project_features (same query as 3)
+ # 14. project.authorizations for user (same query as 5)
+ # 15. current designs by filename and issue
+ # 16, 17 project.authorizations for user (same query as 5)
+ # 18. find route by id and source_type
# ------------- our queries are below:
- # 18. start transaction 1
- # 19. start transaction 2
- # 20. find version by sha and issue
- # 21. exists version with sha and issue?
- # 22. leave transaction 2
- # 23. create version with sha and issue
- # 24. create design-version links
- # 25. validate version.actions.present?
- # 26. validate version.issue.present?
- # 27. validate version.sha is unique
- # 28. leave transaction 1
+ # 19. start transaction 1
+ # 20. start transaction 2
+ # 21. find version by sha and issue
+ # 22. exists version with sha and issue?
+ # 23. leave transaction 2
+ # 24. create version with sha and issue
+ # 25. create design-version links
+ # 26. validate version.actions.present?
+ # 27. validate version.issue.present?
+ # 28. validate version.sha is unique
+ # 29. leave transaction 1
#
- expect { run_mutation }.not_to exceed_query_limit(28)
+ expect { run_mutation }.not_to exceed_query_limit(29)
end
end
diff --git a/spec/graphql/mutations/groups/update_spec.rb b/spec/graphql/mutations/groups/update_spec.rb
new file mode 100644
index 00000000000..2118134e8e6
--- /dev/null
+++ b/spec/graphql/mutations/groups/update_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Groups::Update do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ let(:params) { { full_path: group.full_path } }
+
+ specify { expect(described_class).to require_graphql_authorizations(:admin_group) }
+
+ describe '#resolve' do
+ subject { described_class.new(object: group, context: { current_user: user }, field: nil).resolve(**params) }
+
+ RSpec.shared_examples 'updating the group shared runners setting' do
+ it 'updates the group shared runners setting' do
+ expect { subject }
+ .to change { group.reload.shared_runners_setting }.from('enabled').to('disabled_and_unoverridable')
+ end
+
+ it 'returns no errors' do
+ expect(subject).to eq(errors: [], group: group)
+ end
+
+ context 'with invalid params' do
+ let_it_be(:params) { { full_path: group.full_path, shared_runners_setting: 'inexistent_setting' } }
+
+ it 'doesn\'t update the shared_runners_setting' do
+ expect { subject }
+ .not_to change { group.reload.shared_runners_setting }
+ end
+
+ it 'returns an error' do
+ expect(subject).to eq(
+ group: nil,
+ errors: ["Update shared runners state must be one of: #{::Namespace::SHARED_RUNNERS_SETTINGS.join(', ')}"]
+ )
+ end
+ end
+ end
+
+ RSpec.shared_examples 'denying access to group shared runners setting' do
+ it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
+ end
+
+ context 'changing shared runners setting' do
+ let_it_be(:params) do
+ { full_path: group.full_path,
+ shared_runners_setting: 'disabled_and_unoverridable' }
+ end
+
+ where(:user_role, :shared_examples_name) do
+ :owner | 'updating the group shared runners setting'
+ :developer | 'denying access to group shared runners setting'
+ :reporter | 'denying access to group shared runners setting'
+ :guest | 'denying access to group shared runners setting'
+ :anonymous | 'denying access to group shared runners setting'
+ end
+
+ with_them do
+ before do
+ group.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/issues/update_spec.rb b/spec/graphql/mutations/issues/update_spec.rb
index 80f43338bb5..bb57ad4c404 100644
--- a/spec/graphql/mutations/issues/update_spec.rb
+++ b/spec/graphql/mutations/issues/update_spec.rb
@@ -131,6 +131,28 @@ RSpec.describe Mutations::Issues::Update do
expect(issue.reload.labels).to match_array([project_label, label_2])
end
+
+ context 'when setting labels with label_ids' do
+ it 'replaces existing labels with provided ones' do
+ expect(issue.reload.labels).to match_array([project_label])
+
+ mutation_params[:label_ids] = [label_1.id, label_2.id]
+
+ subject
+
+ expect(issue.reload.labels).to match_array([label_1, label_2])
+ end
+
+ it 'raises error when label_ids is combined with remove_label_ids' do
+ expect { mutation.ready?(label_ids: [label_1.id, label_2.id], remove_label_ids: [label_1.id]) }
+ .to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'labelIds is mutually exclusive with any of addLabelIds or removeLabelIds')
+ end
+
+ it 'raises error when label_ids is combined with add_label_ids' do
+ expect { mutation.ready?(label_ids: [label_1.id, label_2.id], add_label_ids: [label_2.id]) }
+ .to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'labelIds is mutually exclusive with any of addLabelIds or removeLabelIds')
+ end
+ end
end
context 'when changing type' do
diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb
index 8d2ae238bfe..d77a0b6242e 100644
--- a/spec/graphql/resolvers/base_resolver_spec.rb
+++ b/spec/graphql/resolvers/base_resolver_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe Resolvers::BaseResolver do
let(:resolver) do
Class.new(described_class) do
- argument :test, ::GraphQL::INT_TYPE, required: false
- type [::GraphQL::INT_TYPE], null: true
+ argument :test, ::GraphQL::Types::Int, required: false
+ type [::GraphQL::Types::Int], null: true
def resolve(test: 100)
process(object)
@@ -22,7 +22,7 @@ RSpec.describe Resolvers::BaseResolver do
let(:last_resolver) do
Class.new(described_class) do
- type [::GraphQL::INT_TYPE], null: true
+ type [::GraphQL::Types::Int], null: true
def resolve(**args)
[1, 2]
@@ -36,11 +36,11 @@ RSpec.describe Resolvers::BaseResolver do
context 'for a connection of scalars' do
let(:resolver) do
Class.new(described_class) do
- type ::GraphQL::INT_TYPE.connection_type, null: true
+ type ::GraphQL::Types::Int.connection_type, null: true
end
end
- it { is_expected.to eq(::GraphQL::INT_TYPE) }
+ it { is_expected.to eq(::GraphQL::Types::Int) }
end
context 'for a connection of objects' do
@@ -64,21 +64,21 @@ RSpec.describe Resolvers::BaseResolver do
context 'for a list type' do
let(:resolver) do
Class.new(described_class) do
- type [::GraphQL::STRING_TYPE], null: true
+ type [::GraphQL::Types::String], null: true
end
end
- it { is_expected.to eq(::GraphQL::STRING_TYPE) }
+ it { is_expected.to eq(::GraphQL::Types::String) }
end
context 'for a scalar type' do
let(:resolver) do
Class.new(described_class) do
- type ::GraphQL::BOOLEAN_TYPE, null: true
+ type ::GraphQL::Types::Boolean, null: true
end
end
- it { is_expected.to eq(::GraphQL::BOOLEAN_TYPE) }
+ it { is_expected.to eq(::GraphQL::Types::Boolean) }
end
end
@@ -88,7 +88,7 @@ RSpec.describe Resolvers::BaseResolver do
end
it 'has the correct (singular) type' do
- expect(resolver.single.type).to eq(::GraphQL::INT_TYPE)
+ expect(resolver.single.type).to eq(::GraphQL::Types::Int)
end
it 'returns the same subclass every time' do
@@ -105,10 +105,10 @@ RSpec.describe Resolvers::BaseResolver do
describe '.when_single' do
let(:resolver) do
Class.new(described_class) do
- type [::GraphQL::INT_TYPE], null: true
+ type [::GraphQL::Types::Int], null: true
when_single do
- argument :foo, ::GraphQL::INT_TYPE, required: true
+ argument :foo, ::GraphQL::Types::Int, required: true
end
def resolve(foo: 1)
@@ -138,14 +138,14 @@ RSpec.describe Resolvers::BaseResolver do
context 'multiple when_single blocks' do
let(:resolver) do
Class.new(described_class) do
- type [::GraphQL::INT_TYPE], null: true
+ type [::GraphQL::Types::Int], null: true
when_single do
- argument :foo, ::GraphQL::INT_TYPE, required: true
+ argument :foo, ::GraphQL::Types::Int, required: true
end
when_single do
- argument :bar, ::GraphQL::INT_TYPE, required: true
+ argument :bar, ::GraphQL::Types::Int, required: true
end
def resolve(foo: 1, bar: 2)
@@ -168,7 +168,7 @@ RSpec.describe Resolvers::BaseResolver do
let(:subclass) do
Class.new(resolver) do
when_single do
- argument :inc, ::GraphQL::INT_TYPE, required: true
+ argument :inc, ::GraphQL::Types::Int, required: true
end
def resolve(foo:, inc:)
@@ -194,7 +194,7 @@ RSpec.describe Resolvers::BaseResolver do
context 'when the resolver returns early' do
let(:resolver) do
Class.new(described_class) do
- type [::GraphQL::STRING_TYPE], null: true
+ type [::GraphQL::Types::String], null: true
def ready?(**args)
[false, %w[early return]]
@@ -237,14 +237,14 @@ RSpec.describe Resolvers::BaseResolver do
context 'when field is a connection' do
it 'increases complexity based on arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 1)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 1)
expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 3
expect(field.to_graphql.complexity.call({}, { search: 'foo' }, 1)).to eq 7
end
it 'does not increase complexity when filtering by iids' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
expect(field.to_graphql.complexity.call({}, { sort: 'foo' }, 1)).to eq 6
expect(field.to_graphql.complexity.call({}, { sort: 'foo', iid: 1 }, 1)).to eq 3
diff --git a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
index 8d15d7eda1b..852aaf66201 100644
--- a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
+++ b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe ::CachingArrayResolver do
Class.new(::Resolvers::BaseResolver) do
include mod
type [::Types::UserType], null: true
- argument :is_admin, ::GraphQL::BOOLEAN_TYPE, required: false
+ argument :is_admin, ::GraphQL::Types::Boolean, required: false
def query_input(is_admin:)
is_admin
@@ -50,7 +50,7 @@ RSpec.describe ::CachingArrayResolver do
Class.new(::Resolvers::BaseResolver) do
include mod
type [::Types::UserType], null: true
- argument :username, ::GraphQL::STRING_TYPE, required: false
+ argument :username, ::GraphQL::Types::String, required: false
def query_input(username:)
username
diff --git a/spec/graphql/resolvers/concerns/resolves_ids_spec.rb b/spec/graphql/resolvers/concerns/resolves_ids_spec.rb
new file mode 100644
index 00000000000..1dd27c0eff0
--- /dev/null
+++ b/spec/graphql/resolvers/concerns/resolves_ids_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ResolvesIds do
+ # gid://gitlab/Project/6
+ # gid://gitlab/Issue/6
+ # gid://gitlab/Project/6 gid://gitlab/Issue/6
+ context 'with a single project' do
+ let(:ids) { 'gid://gitlab/Project/6' }
+ let(:type) { ::Types::GlobalIDType[::Project] }
+
+ it 'returns the correct array' do
+ expect(resolve_ids).to match_array(['6'])
+ end
+ end
+
+ context 'with a single issue' do
+ let(:ids) { 'gid://gitlab/Issue/9' }
+ let(:type) { ::Types::GlobalIDType[::Issue] }
+
+ it 'returns the correct array' do
+ expect(resolve_ids).to match_array(['9'])
+ end
+ end
+
+ context 'with multiple users' do
+ let(:ids) { ['gid://gitlab/User/7', 'gid://gitlab/User/13', 'gid://gitlab/User/21'] }
+ let(:type) { ::Types::GlobalIDType[::User] }
+
+ it 'returns the correct array' do
+ expect(resolve_ids).to match_array(%w[7 13 21])
+ end
+ end
+
+ def mock_resolver
+ Class.new(GraphQL::Schema::Resolver) { extend ResolvesIds }
+ end
+
+ def resolve_ids
+ mock_resolver.resolve_ids(ids, type)
+ end
+end
diff --git a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
index 3dffda75e08..6f6855c8f84 100644
--- a/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
+++ b/spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe ResolvesPipelines do
end
it 'increases field complexity based on arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: false, max_page_size: 1)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: false, max_page_size: 1)
expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 2
expect(field.to_graphql.complexity.call({}, { sha: 'foo' }, 1)).to eq 4
diff --git a/spec/graphql/resolvers/echo_resolver_spec.rb b/spec/graphql/resolvers/echo_resolver_spec.rb
index 4f48e5e0d7a..59a121ac7de 100644
--- a/spec/graphql/resolvers/echo_resolver_spec.rb
+++ b/spec/graphql/resolvers/echo_resolver_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Resolvers::EchoResolver do
let(:text) { 'Message test' }
specify do
- expect(described_class).to have_non_null_graphql_type(::GraphQL::STRING_TYPE)
+ expect(described_class).to have_non_null_graphql_type(::GraphQL::Types::String)
end
describe '#resolve' do
diff --git a/spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb
index bf8d2139c82..2aef483ac95 100644
--- a/spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb
+++ b/spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do
end
context 'error matched' do
- let(:detailed_error) { build(:detailed_error_tracking_error) }
+ let(:detailed_error) { build(:error_tracking_sentry_detailed_error) }
before do
allow(issue_details_service).to receive(:execute)
diff --git a/spec/graphql/resolvers/groups_resolver_spec.rb b/spec/graphql/resolvers/groups_resolver_spec.rb
new file mode 100644
index 00000000000..e53ca674163
--- /dev/null
+++ b/spec/graphql/resolvers/groups_resolver_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::GroupsResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:group) { create(:group, name: 'public-group') }
+ let_it_be(:private_group) { create(:group, :private, name: 'private-group') }
+ let_it_be(:subgroup1) { create(:group, parent: group, name: 'Subgroup') }
+ let_it_be(:subgroup2) { create(:group, parent: subgroup1, name: 'Test Subgroup 2') }
+ let_it_be(:private_subgroup1) { create(:group, :private, parent: private_group, name: 'Subgroup1') }
+ let_it_be(:private_subgroup2) { create(:group, :private, parent: private_subgroup1, name: 'Subgroup2') }
+ let_it_be(:user) { create(:user) }
+
+ before_all do
+ private_group.add_developer(user)
+ end
+
+ shared_examples 'access to all public descendant groups' do
+ it 'returns all public descendant groups of the parent group ordered by ASC name' do
+ is_expected.to eq([subgroup1, subgroup2])
+ end
+ end
+
+ shared_examples 'access to all public subgroups' do
+ it 'returns all public subgroups of the parent group' do
+ is_expected.to contain_exactly(subgroup1)
+ end
+ end
+
+ shared_examples 'returning empty results' do
+ it 'returns empty results' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when parent group is public' do
+ subject { resolve(described_class, obj: group, args: params, ctx: { current_user: current_user }) }
+
+ context 'when `include_parent_descendants` is false' do
+ let(:params) { { include_parent_descendants: false } }
+
+ context 'when user is not logged in' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'access to all public subgroups'
+ end
+
+ context 'when user is logged in' do
+ let(:current_user) { user }
+
+ it_behaves_like 'access to all public subgroups'
+ end
+ end
+
+ context 'when `include_parent_descendants` is true' do
+ let(:params) { { include_parent_descendants: true } }
+
+ context 'when user is not logged in' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'access to all public descendant groups'
+ end
+
+ context 'when user is logged in' do
+ let(:current_user) { user }
+
+ it_behaves_like 'access to all public descendant groups'
+
+ context 'with owned argument set as true' do
+ before do
+ subgroup1.add_owner(current_user)
+ params[:owned] = true
+ end
+
+ it 'returns only descendant groups owned by the user' do
+ is_expected.to contain_exactly(subgroup1)
+ end
+ end
+
+ context 'with search argument' do
+ it 'returns only descendant groups with matching name or path' do
+ params[:search] = 'Test'
+ is_expected.to contain_exactly(subgroup2)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when parent group is private' do
+ subject { resolve(described_class, obj: private_group, args: params, ctx: { current_user: current_user }) }
+
+ context 'when `include_parent_descendants` is true' do
+ let(:params) { { include_parent_descendants: true } }
+
+ context 'when user is not logged in' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'returning empty results'
+ end
+
+ context 'when user is logged in' do
+ let(:current_user) { user }
+
+ it 'returns all private descendant groups' do
+ is_expected.to contain_exactly(private_subgroup1, private_subgroup2)
+ end
+ end
+ end
+
+ context 'when `include_parent_descendants` is false' do
+ let(:params) { { include_parent_descendants: false } }
+
+ context 'when user is not logged in' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'returning empty results'
+ end
+
+ context 'when user is logged in' do
+ let(:current_user) { user }
+
+ it 'returns private subgroups' do
+ is_expected.to contain_exactly(private_subgroup1)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 9b329e961cc..6e187e57729 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -11,9 +11,9 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:project) { create(:project, group: group) }
let_it_be(:other_project) { create(:project, group: group) }
- let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:started_milestone) { create(:milestone, project: project, title: "started milestone", start_date: 1.day.ago) }
let_it_be(:assignee) { create(:user) }
- let_it_be(:issue1) { create(:incident, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago, milestone: milestone) }
+ let_it_be(:issue1) { create(:incident, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago, milestone: started_milestone) }
let_it_be(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago, assignees: [assignee]) }
let_it_be(:issue3) { create(:issue, project: other_project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago, assignees: [assignee]) }
let_it_be(:issue4) { create(:issue) }
@@ -43,7 +43,63 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'filters by milestone' do
- expect(resolve_issues(milestone_title: [milestone.title])).to contain_exactly(issue1)
+ expect(resolve_issues(milestone_title: [started_milestone.title])).to contain_exactly(issue1)
+ end
+
+ describe 'filtering by milestone wildcard id' do
+ let_it_be(:upcoming_milestone) { create(:milestone, project: project, title: "upcoming milestone", start_date: 1.day.ago, due_date: 1.day.from_now) }
+ let_it_be(:past_milestone) { create(:milestone, project: project, title: "past milestone", due_date: 1.day.ago) }
+ let_it_be(:future_milestone) { create(:milestone, project: project, title: "future milestone", start_date: 1.day.from_now) }
+ let_it_be(:issue5) { create(:issue, project: project, state: :opened, milestone: upcoming_milestone) }
+ let_it_be(:issue6) { create(:issue, project: project, state: :opened, milestone: past_milestone) }
+ let_it_be(:issue7) { create(:issue, project: project, state: :opened, milestone: future_milestone) }
+
+ let(:wildcard_started) { 'STARTED' }
+ let(:wildcard_upcoming) { 'UPCOMING' }
+ let(:wildcard_any) { 'ANY' }
+ let(:wildcard_none) { 'NONE' }
+
+ it 'returns issues with started milestone' do
+ expect(resolve_issues(milestone_wildcard_id: wildcard_started)).to contain_exactly(issue1, issue5)
+ end
+
+ it 'returns issues with upcoming milestone' do
+ expect(resolve_issues(milestone_wildcard_id: wildcard_upcoming)).to contain_exactly(issue5)
+ end
+
+ it 'returns issues with any milestone' do
+ expect(resolve_issues(milestone_wildcard_id: wildcard_any)).to contain_exactly(issue1, issue5, issue6, issue7)
+ end
+
+ it 'returns issues with no milestone' do
+ expect(resolve_issues(milestone_wildcard_id: wildcard_none)).to contain_exactly(issue2)
+ end
+
+ it 'raises a mutually exclusive filter error when wildcard and title are provided' do
+ expect do
+ resolve_issues(milestone_title: ["started milestone"], milestone_wildcard_id: wildcard_started)
+ end.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'only one of [milestoneTitle, milestoneWildcardId] arguments is allowed at the same time.')
+ end
+
+ context 'negated filtering' do
+ it 'returns issues matching the searched title after applying a negated filter' do
+ expect(resolve_issues(milestone_title: ['past milestone'], not: { milestone_wildcard_id: wildcard_upcoming })).to contain_exactly(issue6)
+ end
+
+ it 'returns issues excluding the ones with started milestone' do
+ expect(resolve_issues(not: { milestone_wildcard_id: wildcard_started })).to contain_exactly(issue7)
+ end
+
+ it 'returns issues excluding the ones with upcoming milestone' do
+ expect(resolve_issues(not: { milestone_wildcard_id: wildcard_upcoming })).to contain_exactly(issue6)
+ end
+
+ it 'raises a mutually exclusive filter error when wildcard and title are provided as negated filters' do
+ expect do
+ resolve_issues(not: { milestone_title: ["started milestone"], milestone_wildcard_id: wildcard_started })
+ end.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'only one of [milestoneTitle, milestoneWildcardId] arguments is allowed at the same time.')
+ end
+ end
end
it 'filters by two assignees' do
@@ -169,7 +225,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'returns issues without the specified milestone' do
- expect(resolve_issues(not: { milestone_title: [milestone.title] })).to contain_exactly(issue2)
+ expect(resolve_issues(not: { milestone_title: [started_milestone.title] })).to contain_exactly(issue2)
end
it 'returns issues without the specified assignee_usernames' do
@@ -337,13 +393,13 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iid', :request_store do
- result = batch_sync(max_queries: 4) { resolve_issues(iid: issue1.iid).to_a }
+ result = batch_sync(max_queries: 5) { resolve_issues(iid: issue1.iid).to_a }
expect(result).to contain_exactly(issue1)
end
it 'batches queries that only include IIDs', :request_store do
- result = batch_sync(max_queries: 4) do
+ result = batch_sync(max_queries: 5) do
[issue1, issue2]
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
.flat_map(&:to_a)
@@ -353,7 +409,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'finds a specific issue with iids', :request_store do
- result = batch_sync(max_queries: 4) do
+ result = batch_sync(max_queries: 5) do
resolve_issues(iids: [issue1.iid]).to_a
end
@@ -407,7 +463,7 @@ RSpec.describe Resolvers::IssuesResolver do
end
it 'increases field complexity based on arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 4
expect(field.to_graphql.complexity.call({}, { labelName: 'foo' }, 1)).to eq 8
diff --git a/spec/graphql/resolvers/merge_requests_count_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_count_resolver_spec.rb
new file mode 100644
index 00000000000..da177da93a6
--- /dev/null
+++ b/spec/graphql/resolvers/merge_requests_count_resolver_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::MergeRequestsCountResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project1) { create(:project, :repository, :public) }
+ let_it_be(:project2) { create(:project, :repository, repository_access_level: ProjectFeature::PRIVATE) }
+ let_it_be(:issue) { create(:issue, project: project1) }
+ let_it_be(:merge_request_closing_issue1) { create(:merge_requests_closing_issues, issue: issue) }
+ let_it_be(:merge_request_closing_issue2) do
+ merge_request = create(:merge_request, source_project: project2)
+ create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request)
+ end
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(GraphQL::Types::Int)
+ end
+
+ subject { batch_sync { resolve_merge_requests_count(issue) } }
+
+ context "when user can only view an issue's closing merge requests that are public" do
+ it 'returns the count of the merge requests closing the issue' do
+ expect(subject).to eq(1)
+ end
+ end
+
+ context "when user can view an issue's closing merge requests that are both public and private" do
+ before do
+ project2.add_reporter(user)
+ end
+
+ it 'returns the count of the merge requests closing the issue' do
+ expect(subject).to eq(2)
+ end
+ end
+ end
+
+ def resolve_merge_requests_count(obj)
+ resolve(described_class, obj: obj, ctx: { current_user: user })
+ end
+end
diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
index aec6c6c6708..64ee0d4f9cc 100644
--- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
@@ -303,6 +303,29 @@ RSpec.describe Resolvers::MergeRequestsResolver do
expect { resolve_mr(project, sort: :merged_at_desc, labels: %w[a b]) }.not_to raise_error
end
end
+
+ context 'when sorting by closed at' do
+ before do
+ merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
+ merge_request_3.metrics.update!(latest_closed_at: 5.days.ago)
+ end
+
+ it 'sorts merge requests ascending' do
+ expect(resolve_mr(project, sort: :closed_at_asc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [closed_at(mr), -mr.id] })
+ end
+
+ it 'sorts merge requests descending' do
+ expect(resolve_mr(project, sort: :closed_at_desc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] })
+ end
+
+ def closed_at(mr)
+ nils_last(mr.metrics.latest_closed_at)
+ end
+ end
end
end
end
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index 618d012bd6d..b1f50a4a4a5 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -145,7 +145,7 @@ RSpec.describe Resolvers::NamespaceProjectsResolver do
end
it 'has an high complexity regardless of arguments' do
- field = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
+ field = Types::BaseField.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: described_class, null: false, max_page_size: 100)
expect(field.to_graphql.complexity.call({}, {}, 1)).to eq 24
expect(field.to_graphql.complexity.call({}, { include_subgroups: true }, 1)).to eq 24
diff --git a/spec/graphql/resolvers/paginated_tree_resolver_spec.rb b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
new file mode 100644
index 00000000000..82b05937aa3
--- /dev/null
+++ b/spec/graphql/resolvers/paginated_tree_resolver_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::PaginatedTreeResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository }
+
+ specify do
+ expect(described_class).to have_nullable_graphql_type(Types::Tree::TreeType.connection_type)
+ end
+
+ describe '#resolve', :aggregate_failures do
+ subject { resolve_repository(args, opts) }
+
+ let(:args) { { ref: 'master' } }
+ let(:opts) { {} }
+
+ let(:start_cursor) { subject.start_cursor }
+ let(:end_cursor) { subject.end_cursor }
+ let(:items) { subject.items }
+ let(:entries) { items.first.entries }
+
+ it 'resolves to a collection with a tree object' do
+ expect(items.first).to be_an_instance_of(Tree)
+
+ expect(start_cursor).to be_nil
+ expect(end_cursor).to be_blank
+ expect(entries.count).to eq(repository.tree.entries.count)
+ end
+
+ context 'with recursive option' do
+ let(:args) { super().merge(recursive: true) }
+
+ it 'resolve to a recursive tree' do
+ expect(entries[4].path).to eq('files/html')
+ end
+ end
+
+ context 'with limited max_page_size' do
+ let(:opts) { { max_page_size: 5 } }
+
+ it 'resolves to a pagination collection with a tree object' do
+ expect(items.first).to be_an_instance_of(Tree)
+
+ expect(start_cursor).to be_nil
+ expect(end_cursor).to be_present
+ expect(entries.count).to eq(5)
+ end
+ end
+
+ context 'when repository does not exist' do
+ before do
+ allow(repository).to receive(:exists?).and_return(false)
+ end
+
+ it 'returns nil' do
+ is_expected.to be(nil)
+ end
+ end
+
+ describe 'Cursor pagination' do
+ context 'when cursor is invalid' do
+ let(:args) { super().merge(after: 'invalid') }
+
+ it { expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) }
+ end
+
+ it 'returns all tree entries during cursor pagination' do
+ cursor = nil
+
+ expected_entries = repository.tree.entries.map(&:path)
+ collected_entries = []
+
+ loop do
+ result = resolve_repository(args.merge(after: cursor), max_page_size: 10)
+
+ collected_entries += result.items.first.entries.map(&:path)
+
+ expect(result.start_cursor).to eq(cursor)
+ cursor = result.end_cursor
+
+ break if cursor.blank?
+ end
+
+ expect(collected_entries).to match_array(expected_entries)
+ end
+ end
+ end
+
+ def resolve_repository(args, opts = {})
+ field_options = described_class.field_options.merge(
+ owner: resolver_parent,
+ name: 'field_value'
+ ).merge(opts)
+
+ field = ::Types::BaseField.new(**field_options)
+ resolve_field(field, repository, args: args, object_type: resolver_parent)
+ end
+end
diff --git a/spec/graphql/resolvers/project_resolver_spec.rb b/spec/graphql/resolvers/project_resolver_spec.rb
index 72a01b1c574..d0661c27b95 100644
--- a/spec/graphql/resolvers/project_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_resolver_spec.rb
@@ -28,8 +28,8 @@ RSpec.describe Resolvers::ProjectResolver do
end
it 'does not increase complexity depending on number of load limits' do
- field1 = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 100)
- field2 = Types::BaseField.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: described_class, null: false, max_page_size: 1)
+ field1 = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class, null: false, max_page_size: 100)
+ field2 = Types::BaseField.new(name: 'test', type: GraphQL::Types::String, resolver_class: described_class, null: false, max_page_size: 1)
expect(field1.to_graphql.complexity.call({}, {}, 1)).to eq 2
expect(field2.to_graphql.complexity.call({}, {}, 1)).to eq 2
diff --git a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb
index 8c36153d485..75b9be7dfe7 100644
--- a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb
@@ -86,11 +86,11 @@ RSpec.describe Resolvers::Projects::JiraProjectsResolver do
context 'when Jira connection is not valid' do
before do
WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/project')
- .to_raise(JIRA::HTTPError.new(double(message: 'Some failure.')))
+ .to_raise(JIRA::HTTPError.new(double(message: '{"errorMessages":["Some failure"]}')))
end
it 'raises failure error' do
- expect { resolve_jira_projects }.to raise_error('Jira request error: Some failure.')
+ expect { resolve_jira_projects }.to raise_error('An error occurred while requesting data from Jira: Some failure. Check your Jira integration configuration and try again.')
end
end
end
diff --git a/spec/graphql/resolvers/terraform/states_resolver_spec.rb b/spec/graphql/resolvers/terraform/states_resolver_spec.rb
index 91d48cd782b..012c74ce398 100644
--- a/spec/graphql/resolvers/terraform/states_resolver_spec.rb
+++ b/spec/graphql/resolvers/terraform/states_resolver_spec.rb
@@ -43,7 +43,8 @@ RSpec.describe Resolvers::Terraform::StatesResolver.single do
it do
expect(subject).to be_present
- expect(subject.type.to_s).to eq('String!')
+ expect(subject.type).to be_kind_of GraphQL::Schema::NonNull
+ expect(subject.type.unwrap).to eq GraphQL::Types::String
expect(subject.description).to be_present
end
end
diff --git a/spec/graphql/resolvers/timelog_resolver_spec.rb b/spec/graphql/resolvers/timelog_resolver_spec.rb
index bb4938c751f..f45f528fe7e 100644
--- a/spec/graphql/resolvers/timelog_resolver_spec.rb
+++ b/spec/graphql/resolvers/timelog_resolver_spec.rb
@@ -5,115 +5,306 @@ require 'spec_helper'
RSpec.describe Resolvers::TimelogResolver do
include GraphqlHelpers
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :empty_repo, :public, group: group) }
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:error_class) { Gitlab::Graphql::Errors::ArgumentError }
+
specify do
expect(described_class).to have_non_null_graphql_type(::Types::TimelogType.connection_type)
end
- context "with a group" do
- let_it_be(:current_user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :empty_repo, :public, group: group) }
+ shared_examples_for 'with a project' do
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:timelog1) { create(:issue_timelog, issue: issue, spent_at: 2.days.ago.beginning_of_day) }
+ let_it_be(:timelog2) { create(:issue_timelog, issue: issue, spent_at: 2.days.ago.end_of_day) }
+ let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, spent_at: 10.days.ago) }
- describe '#resolve' do
- let_it_be(:short_time_ago) { 5.days.ago.beginning_of_day }
- let_it_be(:medium_time_ago) { 15.days.ago.beginning_of_day }
+ let(:args) { { start_time: 6.days.ago, end_time: 2.days.ago.noon } }
- let_it_be(:issue) { create(:issue, project: project) }
- let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ it 'finds all timelogs within given dates' do
+ timelogs = resolve_timelogs(**args)
- let_it_be(:timelog1) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.beginning_of_day) }
- let_it_be(:timelog2) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.end_of_day) }
- let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, spent_at: medium_time_ago) }
+ expect(timelogs).to contain_exactly(timelog1)
+ end
- let(:args) { { start_time: short_time_ago, end_time: short_time_ago.noon } }
+ context 'when no dates specified' do
+ let(:args) { {} }
it 'finds all timelogs' do
- timelogs = resolve_timelogs
+ timelogs = resolve_timelogs(**args)
expect(timelogs).to contain_exactly(timelog1, timelog2, timelog3)
end
+ end
- it 'finds all timelogs within given dates' do
+ context 'when only start_time present' do
+ let(:args) { { start_time: 2.days.ago.noon } }
+
+ it 'finds timelogs after the start_time' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog2)
+ end
+ end
+
+ context 'when only end_time present' do
+ let(:args) { { end_time: 2.days.ago.noon } }
+
+ it 'finds timelogs before the end_time' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog3)
+ end
+ end
+
+ context 'when start_time and end_date are present' do
+ let(:args) { { start_time: 6.days.ago, end_date: 2.days.ago } }
+
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog2)
+ end
+ end
+
+ context 'when start_date and end_time are present' do
+ let(:args) { { start_date: 6.days.ago, end_time: 2.days.ago.noon } }
+
+ it 'finds all timelogs within start_date and end_time' do
timelogs = resolve_timelogs(**args)
expect(timelogs).to contain_exactly(timelog1)
end
+ end
- context 'when only start_date is present' do
- let(:args) { { start_date: short_time_ago } }
+ it 'return nothing when user has insufficient permissions' do
+ project2 = create(:project, :empty_repo, :private)
+ issue2 = create(:issue, project: project2)
+ create(:issue_timelog, issue: issue2, spent_at: 2.days.ago.beginning_of_day)
- it 'finds timelogs until the end of day of end_date' do
- timelogs = resolve_timelogs(**args)
+ user = create(:user)
- expect(timelogs).to contain_exactly(timelog1, timelog2)
+ expect(resolve_timelogs(user: user, obj: project2, **args)).to be_empty
+ end
+
+ context 'when arguments are invalid' do
+ let_it_be(:error_class) { Gitlab::Graphql::Errors::ArgumentError }
+
+ context 'when start_time and start_date are present' do
+ let(:args) { { start_time: 6.days.ago, start_date: 6.days.ago } }
+
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Provide either a start date or time, but not both/)
end
end
- context 'when only end_date is present' do
- let(:args) { { end_date: medium_time_ago } }
+ context 'when end_time and end_date are present' do
+ let(:args) { { end_time: 2.days.ago, end_date: 2.days.ago } }
+
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Provide either an end date or time, but not both/)
+ end
+ end
- it 'finds timelogs until the end of day of end_date' do
- timelogs = resolve_timelogs(**args)
+ context 'when start argument is after end argument' do
+ let(:args) { { start_time: 2.days.ago, end_time: 6.days.ago } }
- expect(timelogs).to contain_exactly(timelog3)
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Start argument must be before End argument/)
end
end
+ end
+ end
- context 'when start_time and end_date are present' do
- let(:args) { { start_time: short_time_ago, end_date: short_time_ago } }
+ shared_examples "with a group" do
+ let_it_be(:short_time_ago) { 5.days.ago.beginning_of_day }
+ let_it_be(:medium_time_ago) { 15.days.ago.beginning_of_day }
- it 'finds timelogs until the end of day of end_date' do
- timelogs = resolve_timelogs(**args)
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
- expect(timelogs).to contain_exactly(timelog1, timelog2)
- end
+ let_it_be(:timelog1) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.beginning_of_day) }
+ let_it_be(:timelog2) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.end_of_day) }
+ let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, spent_at: medium_time_ago) }
+
+ let(:args) { { start_time: short_time_ago, end_time: short_time_ago.noon } }
+
+ it 'finds all timelogs' do
+ timelogs = resolve_timelogs
+
+ expect(timelogs).to contain_exactly(timelog1, timelog2, timelog3)
+ end
+
+ it 'finds all timelogs within given dates' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1)
+ end
+
+ context 'when only start_date is present' do
+ let(:args) { { start_date: short_time_ago } }
+
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog2)
end
+ end
- context 'when start_date and end_time are present' do
- let(:args) { { start_date: short_time_ago, end_time: short_time_ago.noon } }
+ context 'when only end_date is present' do
+ let(:args) { { end_date: medium_time_ago } }
- it 'finds all timelogs within start_date and end_time' do
- timelogs = resolve_timelogs(**args)
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
- expect(timelogs).to contain_exactly(timelog1)
- end
+ expect(timelogs).to contain_exactly(timelog3)
end
+ end
- context 'when arguments are invalid' do
- let_it_be(:error_class) { Gitlab::Graphql::Errors::ArgumentError }
+ context 'when start_time and end_date are present' do
+ let(:args) { { start_time: short_time_ago, end_date: short_time_ago } }
- context 'when start_time and start_date are present' do
- let(:args) { { start_time: short_time_ago, start_date: short_time_ago } }
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog2)
+ end
+ end
+
+ context 'when start_date and end_time are present' do
+ let(:args) { { start_date: short_time_ago, end_time: short_time_ago.noon } }
+
+ it 'finds all timelogs within start_date and end_time' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1)
+ end
+ end
+
+ context 'when arguments are invalid' do
+ context 'when start_time and start_date are present' do
+ let(:args) { { start_time: short_time_ago, start_date: short_time_ago } }
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Provide either a start date or time, but not both/)
- end
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Provide either a start date or time, but not both/)
end
+ end
- context 'when end_time and end_date are present' do
- let(:args) { { end_time: short_time_ago, end_date: short_time_ago } }
+ context 'when end_time and end_date are present' do
+ let(:args) { { end_time: short_time_ago, end_date: short_time_ago } }
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Provide either an end date or time, but not both/)
- end
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Provide either an end date or time, but not both/)
end
+ end
- context 'when start argument is after end argument' do
- let(:args) { { start_time: short_time_ago, end_time: medium_time_ago } }
+ context 'when start argument is after end argument' do
+ let(:args) { { start_time: short_time_ago, end_time: medium_time_ago } }
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Start argument must be before End argument/)
- end
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Start argument must be before End argument/)
end
end
end
end
- def resolve_timelogs(user: current_user, **args)
+ shared_examples "with a user" do
+ let_it_be(:short_time_ago) { 5.days.ago.beginning_of_day }
+ let_it_be(:medium_time_ago) { 15.days.ago.beginning_of_day }
+
+ let_it_be(:issue) { create(:issue, project: project) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+
+ let_it_be(:timelog1) { create(:issue_timelog, issue: issue, user: current_user) }
+ let_it_be(:timelog2) { create(:issue_timelog, issue: issue, user: create(:user)) }
+ let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, user: current_user) }
+
+ it 'blah' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog3)
+ end
+ end
+
+ context "on a project" do
+ let(:object) { project }
+ let(:extra_args) { {} }
+
+ it_behaves_like 'with a project'
+ end
+
+ context "with a project filter" do
+ let(:object) { nil }
+ let(:extra_args) { { project_id: project.to_global_id } }
+
+ it_behaves_like 'with a project'
+ end
+
+ context 'on a group' do
+ let(:object) { group }
+ let(:extra_args) { {} }
+
+ it_behaves_like 'with a group'
+ end
+
+ context 'with a group filter' do
+ let(:object) { nil }
+ let(:extra_args) { { group_id: group.to_global_id } }
+
+ it_behaves_like 'with a group'
+ end
+
+ context 'on a user' do
+ let(:object) { current_user }
+ let(:extra_args) { {} }
+ let(:args) { {} }
+
+ it_behaves_like 'with a user'
+ end
+
+ context 'with a user filter' do
+ let(:object) { nil }
+ let(:extra_args) { { username: current_user.username } }
+ let(:args) { {} }
+
+ it_behaves_like 'with a user'
+ end
+
+ context 'when > `default_max_page_size` records' do
+ let(:object) { nil }
+ let!(:timelog_list) { create_list(:timelog, 101, issue: issue) }
+ let(:args) { { project_id: "gid://gitlab/Project/#{project.id}" } }
+ let(:extra_args) { {} }
+
+ it 'pagination returns `default_max_page_size` and sets `has_next_page` true' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs.items.count).to be(100)
+ expect(timelogs.has_next_page).to be(true)
+ end
+ end
+
+ context 'when no object or arguments provided' do
+ let(:object) { nil }
+ let(:args) { {} }
+ let(:extra_args) { {} }
+
+ it 'returns correct error' do
+ expect { resolve_timelogs(**args) }
+ .to raise_error(error_class, /Provide at least one argument/)
+ end
+ end
+
+ def resolve_timelogs(user: current_user, obj: object, **args)
context = { current_user: user }
- resolve(described_class, obj: group, args: args, ctx: context)
+ resolve(described_class, obj: obj, args: args.merge(extra_args), ctx: context)
end
end
diff --git a/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb
index cc855bbcb53..70f06b58a65 100644
--- a/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb
+++ b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Resolvers::UserDiscussionsCountResolver do
let_it_be(:private_discussion) { create_list(:discussion_note_on_issue, 3, noteable: private_issue, project: private_project) }
specify do
- expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE)
+ expect(described_class).to have_nullable_graphql_type(GraphQL::Types::Int)
end
context 'when counting discussions from a public issue' do
diff --git a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb
index 6cf23a2f57f..bc173b2a166 100644
--- a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb
+++ b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Resolvers::UserNotesCountResolver do
let_it_be(:private_project) { create(:project, :repository, :private) }
specify do
- expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE)
+ expect(described_class).to have_nullable_graphql_type(GraphQL::Types::Int)
end
context 'when counting notes from an issue' do
diff --git a/spec/graphql/subscriptions/issuable_updated_spec.rb b/spec/graphql/subscriptions/issuable_updated_spec.rb
index cc88b37627d..c15b4f532ef 100644
--- a/spec/graphql/subscriptions/issuable_updated_spec.rb
+++ b/spec/graphql/subscriptions/issuable_updated_spec.rb
@@ -40,7 +40,7 @@ RSpec.describe Subscriptions::IssuableUpdated do
end
end
- context 'when a GraphQL::ID_TYPE is provided' do
+ context 'when a GraphQL::Types::ID is provided' do
let(:issuable_id) { issue.to_gid.to_s }
it 'raises an exception' do
diff --git a/spec/graphql/types/base_argument_spec.rb b/spec/graphql/types/base_argument_spec.rb
index 61e0179ff21..8f5f2e08799 100644
--- a/spec/graphql/types/base_argument_spec.rb
+++ b/spec/graphql/types/base_argument_spec.rb
@@ -3,15 +3,41 @@
require 'spec_helper'
RSpec.describe Types::BaseArgument do
- include_examples 'Gitlab-style deprecations' do
- let_it_be(:field) do
- Types::BaseField.new(name: 'field', type: String, null: true)
+ let_it_be(:field) do
+ Types::BaseField.new(name: 'field', type: String, null: true)
+ end
+
+ let(:base_args) { { name: 'test', type: String, required: false, owner: field } }
+
+ def subject(args = {})
+ described_class.new(**base_args.merge(args))
+ end
+
+ include_examples 'Gitlab-style deprecations'
+
+ describe 'required argument declarations' do
+ it 'accepts nullable, required arguments' do
+ arguments = base_args.merge({ required: :nullable })
+
+ expect { subject(arguments) }.not_to raise_error
+ end
+
+ it 'accepts required, non-nullable arguments' do
+ arguments = base_args.merge({ required: true })
+
+ expect { subject(arguments) }.not_to raise_error
+ end
+
+ it 'accepts non-required arguments' do
+ arguments = base_args.merge({ required: false })
+
+ expect { subject(arguments) }.not_to raise_error
end
- let(:base_args) { { name: 'test', type: String, required: false, owner: field } }
+ it 'accepts no required argument declaration' do
+ arguments = base_args
- def subject(args = {})
- described_class.new(**base_args.merge(args))
+ expect { subject(arguments) }.not_to raise_error
end
end
end
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index c34fbf42dd8..82efd618e38 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Types::BaseField do
end
it 'defaults to 1' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true)
expect(field.to_graphql.complexity).to eq 1
end
@@ -25,7 +25,7 @@ RSpec.describe Types::BaseField do
describe '#base_complexity' do
context 'with no gitaly calls' do
it 'defaults to 1' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true)
expect(field.base_complexity).to eq 1
end
@@ -33,7 +33,7 @@ RSpec.describe Types::BaseField do
context 'with a gitaly call' do
it 'adds 1 to the default value' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, calls_gitaly: true)
expect(field.base_complexity).to eq 2
end
@@ -41,14 +41,14 @@ RSpec.describe Types::BaseField do
end
it 'has specified value' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
expect(field.to_graphql.complexity).to eq 12
end
context 'when field has a resolver' do
context 'when a valid complexity is already set' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: resolver, complexity: 2, max_page_size: 100, null: true) }
+ let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: resolver, complexity: 2, max_page_size: 100, null: true) }
it 'uses this complexity' do
expect(field.to_graphql.complexity).to eq 2
@@ -56,7 +56,7 @@ RSpec.describe Types::BaseField do
end
context 'and is a connection' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE.connection_type, resolver_class: resolver, max_page_size: 100, null: true) }
+ let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String.connection_type, resolver_class: resolver, max_page_size: 100, null: true) }
it 'sets complexity depending on arguments for resolvers' do
expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 4
@@ -71,7 +71,7 @@ RSpec.describe Types::BaseField do
context 'and is not a connection' do
it 'sets complexity as normal' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, max_page_size: 100, null: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, max_page_size: 100, null: true)
expect(field.to_graphql.complexity.call({}, {}, 2)).to eq 2
expect(field.to_graphql.complexity.call({}, { first: 50 }, 2)).to eq 2
@@ -82,8 +82,8 @@ RSpec.describe Types::BaseField do
context 'calls_gitaly' do
context 'for fields with a resolver' do
it 'adds 1 if true' do
- with_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true, calls_gitaly: true)
- without_gitaly_field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, resolver_class: resolver, null: true)
+ with_gitaly_field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: true, calls_gitaly: true)
+ without_gitaly_field = described_class.new(name: 'test', type: GraphQL::Types::String, resolver_class: resolver, null: true)
base_result = without_gitaly_field.to_graphql.complexity.call({}, {}, 2)
expect(with_gitaly_field.to_graphql.complexity.call({}, {}, 2)).to eq base_result + 1
@@ -92,28 +92,28 @@ RSpec.describe Types::BaseField do
context 'for fields without a resolver' do
it 'adds 1 if true' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, calls_gitaly: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, calls_gitaly: true)
expect(field.to_graphql.complexity).to eq 2
end
end
it 'defaults to false' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true)
expect(field.base_complexity).to eq Types::BaseField::DEFAULT_COMPLEXITY
end
context 'with declared constant complexity value' do
it 'has complexity set to that constant' do
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
expect(field.to_graphql.complexity).to eq 12
end
it 'does not raise an error even with Gitaly calls' do
allow(Gitlab::GitalyClient).to receive(:get_request_count).and_return([0, 1])
- field = described_class.new(name: 'test', type: GraphQL::STRING_TYPE, null: true, complexity: 12)
+ field = described_class.new(name: 'test', type: GraphQL::Types::String, null: true, complexity: 12)
expect(field.to_graphql.complexity).to eq 12
end
@@ -123,7 +123,7 @@ RSpec.describe Types::BaseField do
describe '#visible?' do
context 'and has a feature_flag' do
let(:flag) { :test_feature }
- let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false) }
+ let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, feature_flag: flag, null: false) }
let(:context) { {} }
before do
@@ -156,7 +156,7 @@ RSpec.describe Types::BaseField do
describe '#description' do
context 'feature flag given' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::STRING_TYPE, feature_flag: flag, null: false, description: 'Test description.') }
+ let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, feature_flag: flag, null: false, description: 'Test description.') }
let(:flag) { :test_flag }
it 'prepends the description' do
@@ -211,7 +211,7 @@ RSpec.describe Types::BaseField do
include_examples 'Gitlab-style deprecations' do
def subject(args = {})
- base_args = { name: 'test', type: GraphQL::STRING_TYPE, null: true }
+ base_args = { name: 'test', type: GraphQL::Types::String, null: true }
described_class.new(**base_args.merge(args))
end
diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb
index cdf09dd9cc9..4efa3018dad 100644
--- a/spec/graphql/types/global_id_type_spec.rb
+++ b/spec/graphql/types/global_id_type_spec.rb
@@ -255,7 +255,7 @@ RSpec.describe Types::GlobalIDType do
query(GraphQL.parse(gql_query), vars).result
end
- all_types = [::GraphQL::ID_TYPE, ::Types::GlobalIDType, ::Types::GlobalIDType[::Project]]
+ all_types = [::GraphQL::Types::ID, ::Types::GlobalIDType, ::Types::GlobalIDType[::Project]]
shared_examples 'a working query' do
# Simplified schema to test compatibility
@@ -284,7 +284,7 @@ RSpec.describe Types::GlobalIDType do
# This is needed so that all types are always registered as input types
field :echo, String, null: true do
- argument :id, ::GraphQL::ID_TYPE, required: false
+ argument :id, ::GraphQL::Types::ID, required: false
argument :gid, ::Types::GlobalIDType, required: false
argument :pid, ::Types::GlobalIDType[::Project], required: false
end
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index ef11e3d309c..33250f8e6af 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe GitlabSchema.types['Group'] do
two_factor_grace_period auto_devops_enabled emails_disabled
mentions_disabled parent boards milestones group_members
merge_requests container_repositories container_repositories_count
- packages
+ packages shared_runners_setting timelogs
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -39,6 +39,15 @@ RSpec.describe GitlabSchema.types['Group'] do
it { is_expected.to have_graphql_resolver(Resolvers::GroupMembersResolver) }
end
+ describe 'timelogs field' do
+ subject { described_class.fields['timelogs'] }
+
+ it 'finds timelogs between start time and end time' do
+ is_expected.to have_graphql_resolver(Resolvers::TimelogResolver)
+ is_expected.to have_non_null_graphql_type(Types::TimelogType.connection_type)
+ end
+ end
+
it_behaves_like 'a GraphQL type with labels' do
let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups, :includeDescendantGroups, :onlyGroupLabels] }
end
diff --git a/spec/graphql/types/issue_type_spec.rb b/spec/graphql/types/issue_type_spec.rb
index a117741b3a2..b0aa11ee5ad 100644
--- a/spec/graphql/types/issue_type_spec.rb
+++ b/spec/graphql/types/issue_type_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe GitlabSchema.types['Issue'] do
it 'has specific fields' do
fields = %i[id iid title description state reference author assignees updated_by participants labels milestone due_date
- confidential discussion_locked upvotes downvotes user_notes_count user_discussions_count web_path web_url relative_position
+ confidential discussion_locked upvotes downvotes merge_requests_count 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 timelogs project_id]
diff --git a/spec/graphql/types/merge_requests/reviewer_type_spec.rb b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
index c2182e9968c..4ede8e5788f 100644
--- a/spec/graphql/types/merge_requests/reviewer_type_spec.rb
+++ b/spec/graphql/types/merge_requests/reviewer_type_spec.rb
@@ -31,6 +31,8 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewer'] do
starredProjects
callouts
merge_request_interaction
+ namespace
+ timelogs
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/namespace_type_spec.rb b/spec/graphql/types/namespace_type_spec.rb
index 2ed1ee3e8c4..3b7f7e65e4b 100644
--- a/spec/graphql/types/namespace_type_spec.rb
+++ b/spec/graphql/types/namespace_type_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['Namespace'] do
it 'has the expected fields' do
expected_fields = %w[
id name path full_name full_path description description_html visibility
- lfs_enabled request_access_enabled projects root_storage_statistics
+ lfs_enabled request_access_enabled projects root_storage_statistics shared_runners_setting
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/packages/nuget/dependency_link_metdatum_type_spec.rb b/spec/graphql/types/packages/nuget/dependency_link_metdatum_type_spec.rb
new file mode 100644
index 00000000000..b11d9d131aa
--- /dev/null
+++ b/spec/graphql/types/packages/nuget/dependency_link_metdatum_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['NugetDependencyLinkMetadata'] do
+ it 'includes nuget dependency link metadatum fields' do
+ expected_fields = %w[
+ id target_framework
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/package_dependency_link_type_spec.rb b/spec/graphql/types/packages/package_dependency_link_type_spec.rb
new file mode 100644
index 00000000000..53ee8be69a6
--- /dev/null
+++ b/spec/graphql/types/packages/package_dependency_link_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PackageDependencyLink'] do
+ it 'includes package file fields' do
+ expected_fields = %w[
+ id dependency_type dependency metadata
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/package_dependency_type_enum_spec.rb b/spec/graphql/types/packages/package_dependency_type_enum_spec.rb
new file mode 100644
index 00000000000..b8893a3619e
--- /dev/null
+++ b/spec/graphql/types/packages/package_dependency_type_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PackageDependencyType'] do
+ it 'exposes all depeendency type values' do
+ expect(described_class.values.keys).to contain_exactly(*%w[DEPENDENCIES DEV_DEPENDENCIES BUNDLE_DEPENDENCIES PEER_DEPENDENCIES])
+ end
+end
diff --git a/spec/graphql/types/packages/package_dependency_type_spec.rb b/spec/graphql/types/packages/package_dependency_type_spec.rb
new file mode 100644
index 00000000000..67474729781
--- /dev/null
+++ b/spec/graphql/types/packages/package_dependency_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PackageDependency'] do
+ it 'includes package file fields' do
+ expected_fields = %w[
+ id name version_pattern
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/packages/package_details_type_spec.rb b/spec/graphql/types/packages/package_details_type_spec.rb
index 06093813315..7e1103d8aa0 100644
--- a/spec/graphql/types/packages/package_details_type_spec.rb
+++ b/spec/graphql/types/packages/package_details_type_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
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 versions package_files
+ id name version created_at updated_at package_type tags project pipelines versions package_files dependency_links
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/permission_types/base_permission_type_spec.rb b/spec/graphql/types/permission_types/base_permission_type_spec.rb
index 68632a509ee..e4726ad0e6e 100644
--- a/spec/graphql/types/permission_types/base_permission_type_spec.rb
+++ b/spec/graphql/types/permission_types/base_permission_type_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Types::PermissionTypes::BasePermissionType do
expected_keywords = {
name: :resolve_using_hash,
hash_key: :the_key,
- type: GraphQL::BOOLEAN_TYPE,
+ type: GraphQL::Types::Boolean,
description: "custom description",
null: false
}
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index a22110e8338..d825bd7ebd4 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe GitlabSchema.types['Project'] do
issue_status_counts terraform_states alert_management_integrations
container_repositories container_repositories_count
pipeline_analytics squash_read_only sast_ci_configuration
- ci_template
+ ci_template timelogs
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -392,6 +392,15 @@ RSpec.describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_resolver(Resolvers::Terraform::StatesResolver) }
end
+ describe 'timelogs field' do
+ subject { described_class.fields['timelogs'] }
+
+ it 'finds timelogs for project' do
+ is_expected.to have_graphql_resolver(Resolvers::TimelogResolver)
+ is_expected.to have_graphql_type(Types::TimelogType.connection_type)
+ end
+ end
+
it_behaves_like 'a GraphQL type with labels' do
let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups] }
end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index 9a8f2090cc1..6a43867f1fe 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe GitlabSchema.types['Query'] do
runner_platforms
runner
runners
+ timelogs
]
expect(described_class).to have_graphql_fields(*expected_fields).at_least
@@ -125,4 +126,14 @@ RSpec.describe GitlabSchema.types['Query'] do
it { is_expected.to have_graphql_type(Types::Packages::PackageDetailsType) }
end
+
+ describe 'timelogs field' do
+ subject { described_class.fields['timelogs'] }
+
+ it 'returns timelogs' do
+ is_expected.to have_graphql_arguments(:startDate, :endDate, :startTime, :endTime, :username, :projectId, :groupId, :after, :before, :first, :last)
+ is_expected.to have_graphql_type(Types::TimelogType.connection_type)
+ is_expected.to have_graphql_resolver(Resolvers::TimelogResolver)
+ end
+ end
end
diff --git a/spec/graphql/types/range_input_type_spec.rb b/spec/graphql/types/range_input_type_spec.rb
index aa6fd72cf13..ca27527c2b5 100644
--- a/spec/graphql/types/range_input_type_spec.rb
+++ b/spec/graphql/types/range_input_type_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe ::Types::RangeInputType do
- let(:of_integer) { ::GraphQL::INT_TYPE }
+ let(:of_integer) { ::GraphQL::Types::Int }
context 'parameterized on Integer' do
let(:type) { described_class[of_integer] }
@@ -32,12 +32,12 @@ RSpec.describe ::Types::RangeInputType do
expect(instance).to be_a_kind_of(described_class)
expect(instance).to be_a_kind_of(described_class[of_integer])
- expect(instance).not_to be_a_kind_of(described_class[GraphQL::ID_TYPE])
+ expect(instance).not_to be_a_kind_of(described_class[GraphQL::Types::ID])
end
it 'follows expected subtyping relationships for classes' do
expect(described_class[of_integer]).to be < described_class
- expect(described_class[of_integer]).not_to be < described_class[GraphQL::ID_TYPE]
+ expect(described_class[of_integer]).not_to be < described_class[GraphQL::Types::ID]
expect(described_class[of_integer]).not_to be < described_class[of_integer, false]
end
end
diff --git a/spec/graphql/types/repository_type_spec.rb b/spec/graphql/types/repository_type_spec.rb
index ee0cc4361da..5488d78b720 100644
--- a/spec/graphql/types/repository_type_spec.rb
+++ b/spec/graphql/types/repository_type_spec.rb
@@ -11,6 +11,8 @@ RSpec.describe GitlabSchema.types['Repository'] do
specify { expect(described_class).to have_graphql_field(:tree) }
+ specify { expect(described_class).to have_graphql_field(:paginated_tree, calls_gitaly?: true, max_page_size: 100) }
+
specify { expect(described_class).to have_graphql_field(:exists, calls_gitaly?: true, complexity: 2) }
specify { expect(described_class).to have_graphql_field(:blobs) }
diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb
index 1344af89fb6..dc1b1e2253e 100644
--- a/spec/graphql/types/timelog_type_spec.rb
+++ b/spec/graphql/types/timelog_type_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe GitlabSchema.types['Timelog'] do
- let(:fields) { %i[spent_at time_spent user issue merge_request note] }
+ let(:fields) { %i[spent_at time_spent user issue merge_request note summary] }
it { expect(described_class.graphql_name).to eq('Timelog') }
it { expect(described_class).to have_graphql_fields(fields) }
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index 7d73727b041..363ccdf88b7 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -36,6 +36,8 @@ RSpec.describe GitlabSchema.types['User'] do
projectMemberships
starredProjects
callouts
+ namespace
+ timelogs
]
expect(described_class).to have_graphql_fields(*expected_fields)
@@ -57,4 +59,13 @@ RSpec.describe GitlabSchema.types['User'] do
is_expected.to have_graphql_type(Types::UserCalloutType.connection_type)
end
end
+
+ describe 'timelogs field' do
+ subject { described_class.fields['timelogs'] }
+
+ it 'returns user timelogs' do
+ is_expected.to have_graphql_resolver(Resolvers::TimelogResolver)
+ is_expected.to have_graphql_type(Types::TimelogType.connection_type)
+ end
+ end
end