Commit 7d1278ad authored by Alex Kalderimis's avatar Alex Kalderimis

Prepare tests for framework authorization changes

This mostly adds connection methods in preparation for the
graphql helper changes.

None of the changes in this commit should cause any observable
effects on application behaviour.

This includes

- removal of pagination tests from resolver specs (resolvers do not -
  currently - know enough of their arguments to make this work). At the
  moment we need to run all our pagination specs at the request
  integration level.

  This is the same for N+1 specs must always be run in request specs,
  since otherwise they are unreliable.
parent 4106da77
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
module Types module Types
module AlertManagement module AlertManagement
class PrometheusIntegrationType < BaseObject class PrometheusIntegrationType < ::Types::BaseObject
include ::Gitlab::Routing include ::Gitlab::Routing
graphql_name 'AlertManagementPrometheusIntegration' graphql_name 'AlertManagementPrometheusIntegration'
......
...@@ -5,6 +5,8 @@ module Gitlab ...@@ -5,6 +5,8 @@ module Gitlab
module Aggregations module Aggregations
module VulnerabilityStatistics module VulnerabilityStatistics
class LazyAggregate class LazyAggregate
include ::Gitlab::Graphql::Deferred
attr_reader :vulnerable, :lazy_state attr_reader :vulnerable, :lazy_state
def initialize(query_ctx, vulnerable, include_subgroups: false) def initialize(query_ctx, vulnerable, include_subgroups: false)
......
...@@ -23,11 +23,11 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -23,11 +23,11 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:weight_issue4) { create(:issue, project: project, weight: nil) } let_it_be(:weight_issue4) { create(:issue, project: project, weight: nil) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :weight_asc)).to eq [weight_issue3, weight_issue1, weight_issue4, weight_issue2] expect(resolve_issues(sort: :weight_asc).to_a).to eq [weight_issue3, weight_issue1, weight_issue4, weight_issue2]
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :weight_desc)).to eq [weight_issue1, weight_issue3, weight_issue4, weight_issue2] expect(resolve_issues(sort: :weight_desc).to_a).to eq [weight_issue1, weight_issue3, weight_issue4, weight_issue2]
end end
end end
...@@ -36,11 +36,11 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -36,11 +36,11 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:published) { create(:issue, :published, project: project) } let_it_be(:published) { create(:issue, :published, project: project) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :published_asc)).to eq [not_published, published] expect(resolve_issues(sort: :published_asc).to_a).to eq [not_published, published]
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :published_desc)).to eq [published, not_published] expect(resolve_issues(sort: :published_desc).to_a).to eq [published, not_published]
end end
end end
...@@ -54,11 +54,11 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -54,11 +54,11 @@ RSpec.describe Resolvers::IssuesResolver do
end end
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :sla_due_at_asc)).to eq [sla_due_first, sla_due_last] expect(resolve_issues(sort: :sla_due_at_asc).to_a).to eq [sla_due_first, sla_due_last]
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :sla_due_at_desc)).to eq [sla_due_last, sla_due_first] expect(resolve_issues(sort: :sla_due_at_desc).to_a).to eq [sla_due_last, sla_due_first]
end end
end end
end end
...@@ -69,7 +69,7 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -69,7 +69,7 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be(:issue_without_iteration) { create(:issue, project: project) } let_it_be(:issue_without_iteration) { create(:issue, project: project) }
it 'returns issues with iteration' do it 'returns issues with iteration' do
expect(resolve_issues(iteration_id: iteration1.id)).to eq [issue_with_iteration] expect(resolve_issues(iteration_id: [iteration1.id])).to contain_exactly(issue_with_iteration)
end end
end end
......
...@@ -69,7 +69,7 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do ...@@ -69,7 +69,7 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do
it 'finds only epics for issues matching issue filters' do it 'finds only epics for issues matching issue filters' do
result = resolve_board_epics( result = resolve_board_epics(
group_board, { issue_filters: { label_name: label1.title, not: { label_name: label2.title } } }) group_board, { issue_filters: { label_name: [label1.title], not: { label_name: [label2.title] } } })
expect(result).to match_array([epic1]) expect(result).to match_array([epic1])
end end
...@@ -82,13 +82,14 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do ...@@ -82,13 +82,14 @@ RSpec.describe Resolvers::BoardGroupings::EpicsResolver do
end end
it 'accepts negated issue params' do it 'accepts negated issue params' do
filters = { label_name: ['foo'], not: { label_name: %w(foo bar) } }
expect(Boards::Issues::ListService).to receive(:new).with( expect(Boards::Issues::ListService).to receive(:new).with(
group_board.resource_parent, group_board.resource_parent,
current_user, current_user,
{ all_lists: true, board_id: group_board.id, label_name: 'foo', not: { label_name: %w(foo bar) } } { all_lists: true, board_id: group_board.id, **filters }
).and_call_original ).and_call_original
resolve_board_epics(group_board, { issue_filters: { label_name: 'foo', not: { label_name: %w(foo bar) } } }) resolve_board_epics(group_board, { issue_filters: filters })
end end
it 'raises an exception if both epic_id and epic_wildcard_id are present' do it 'raises an exception if both epic_id and epic_wildcard_id are present' do
......
...@@ -14,32 +14,39 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do ...@@ -14,32 +14,39 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do
end end
describe '#resolve' do describe '#resolve' do
let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) }
let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
let_it_be(:developer) { create(:user, developer_projects: [project]) }
let_it_be(:agents) { create_list(:cluster_agent, 2, project: project) }
let(:finder) { double(execute: relation) } let(:ctx) { { current_user: current_user } }
let(:relation) { double }
let(:project) { create(:project) }
let(:args) { Hash(key: 'value') }
let(:ctx) { Hash(current_user: user) }
let(:lookahead) do before do
double(selects?: true).tap do |selection| stub_licensed_features(cluster_agents: true)
allow(selection).to receive(:selection).and_return(selection)
end
end end
subject { resolve(described_class, obj: project, args: args.merge(lookahead: lookahead), ctx: ctx) } subject { resolve_agents }
context 'the current user has access to clusters' do
let(:current_user) { maintainer }
it 'calls the agents finder' do it 'finds all agents' do
expect(::Clusters::AgentsFinder).to receive(:new) expect(subject).to match_array(agents)
.with(project, user, params: args).and_return(finder) end
end
expect(relation).to receive(:preload) context 'the current user does not have access to clusters' do
.with(:agent_tokens).and_return(relation) let(:current_user) { developer }
expect(subject).to eq(relation) it 'returns an empty result' do
expect(subject).to be_empty
end
end end
end end
def resolve_agents(args = {})
resolve(described_class, obj: project, ctx: ctx, lookahead: positive_lookahead, args: args)
end
end end
RSpec.describe Resolvers::Clusters::AgentsResolver.single do RSpec.describe Resolvers::Clusters::AgentsResolver.single do
......
...@@ -21,36 +21,34 @@ RSpec.describe Resolvers::EpicIssuesResolver do ...@@ -21,36 +21,34 @@ RSpec.describe Resolvers::EpicIssuesResolver do
let_it_be(:epic_issue3) { create(:epic_issue, epic: epic2, issue: issue3, relative_position: 1) } let_it_be(:epic_issue3) { create(:epic_issue, epic: epic2, issue: issue3, relative_position: 1) }
let_it_be(:epic_issue4) { create(:epic_issue, epic: epic2, issue: issue4, relative_position: nil) } let_it_be(:epic_issue4) { create(:epic_issue, epic: epic2, issue: issue4, relative_position: nil) }
specify do
expect(described_class).to have_nullable_graphql_type(Types::EpicIssueType.connection_type)
end
before do before do
group.add_developer(current_user) group.add_developer(current_user)
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
end end
specify do
expect(described_class).to have_nullable_graphql_type(Types::EpicIssueType.connection_type)
end
describe '#resolve' do describe '#resolve' do
let(:epics) { [epic1, epic2] }
it 'finds all epic issues' do it 'finds all epic issues' do
result = [resolve_epic_issues(epic1), resolve_epic_issues(epic2)] result = epics.map { |epic| resolve_epic_issues(epic).to_a }
expect(result).to contain_exactly([issue2, issue1], [issue3, issue4]) expect(result).to eq [[issue2, issue1], [issue3, issue4]]
end end
it 'finds only epic issues that user can read' do it 'finds only epic issues that user can read' do
guest = create(:user) guest = create(:user)
result = result = epics.map { |e| resolve_epic_issues(e, user: guest).to_a }
[
resolve_epic_issues(epic1, {}, { current_user: guest }),
resolve_epic_issues(epic2, {}, { current_user: guest })
]
expect(result).to contain_exactly([], [issue1]) expect(result).to eq [[issue1], []]
end end
end end
def resolve_epic_issues(object, args = {}, context = { current_user: current_user }) def resolve_epic_issues(object, user: current_user)
resolve(described_class, obj: object, args: args, ctx: context) resolve(described_class, obj: object, ctx: { current_user: user })
end end
end end
...@@ -44,7 +44,7 @@ RSpec.describe Resolvers::EpicsResolver do ...@@ -44,7 +44,7 @@ RSpec.describe Resolvers::EpicsResolver do
context 'with iids' do context 'with iids' do
it 'finds a specific epic with iids' do it 'finds a specific epic with iids' do
expect(resolve_epics(iids: epic1.iid)).to contain_exactly(epic1) expect(resolve_epics(iids: [epic1.iid.to_s])).to contain_exactly(epic1)
end end
it 'finds multiple epics with iids' do it 'finds multiple epics with iids' do
......
...@@ -45,11 +45,11 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do ...@@ -45,11 +45,11 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do
describe 'sorting' do describe 'sorting' do
context 'when sorting by created_at' do context 'when sorting by created_at' do
it 'sorts requirements ascending' do it 'sorts requirements ascending' do
expect(resolve_requirements(sort: 'created_asc')).to eq([requirement1, requirement3, requirement2]) expect(resolve_requirements(sort: 'created_asc').to_a).to eq([requirement1, requirement3, requirement2])
end end
it 'sorts requirements descending' do it 'sorts requirements descending' do
expect(resolve_requirements(sort: 'created_desc')).to eq([requirement2, requirement3, requirement1]) expect(resolve_requirements(sort: 'created_desc').to_a).to eq([requirement2, requirement3, requirement1])
end end
end end
end end
...@@ -89,7 +89,7 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do ...@@ -89,7 +89,7 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do
context 'single author exists' do context 'single author exists' do
let(:params) do let(:params) do
{ author_username: other_user.username } { author_username: [other_user.username] }
end end
it 'filters requirements by author' do it 'filters requirements by author' do
...@@ -99,7 +99,7 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do ...@@ -99,7 +99,7 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do
context 'single nonexistent author' do context 'single nonexistent author' do
let(:params) do let(:params) do
{ author_username: "nonsense" } { author_username: ["nonsense"] }
end end
it_behaves_like 'returns no items' it_behaves_like 'returns no items'
...@@ -121,14 +121,6 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do ...@@ -121,14 +121,6 @@ RSpec.describe Resolvers::RequirementsManagement::RequirementsResolver do
it_behaves_like 'returns unfiltered' it_behaves_like 'returns unfiltered'
end end
context 'single author is nil' do
let(:params) do
{ author_username: nil }
end
it_behaves_like 'returns unfiltered'
end
context 'an empty array' do context 'an empty array' do
let(:params) do let(:params) do
{ author_username: [] } { author_username: [] }
......
...@@ -29,10 +29,10 @@ RSpec.describe Resolvers::SecurityReportSummaryResolver do ...@@ -29,10 +29,10 @@ RSpec.describe Resolvers::SecurityReportSummaryResolver do
pipeline, pipeline,
expected_selection_info expected_selection_info
) do |summary_service| ) do |summary_service|
expect(summary_service).to receive(:execute) expect(summary_service).to receive(:execute).and_return({})
end end
resolve(described_class, obj: pipeline, args: { lookahead: lookahead }) resolve(described_class, obj: pipeline, lookahead: lookahead)
end end
end end
end end
......
...@@ -4,8 +4,11 @@ require 'spec_helper' ...@@ -4,8 +4,11 @@ require 'spec_helper'
RSpec.describe Resolvers::VulnerabilitiesGradeResolver do RSpec.describe Resolvers::VulnerabilitiesGradeResolver do
include GraphqlHelpers include GraphqlHelpers
include ::Gitlab::Graphql::Laziness
subject { resolve(described_class, obj: group, args: args, ctx: { current_user: user }).execute } subject do
force(resolve(described_class, obj: group, args: args, ctx: { current_user: user }))
end
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) } let_it_be(:subgroup) { create(:group, parent: group) }
......
...@@ -34,15 +34,15 @@ RSpec.describe 'Getting issues for an epic' do ...@@ -34,15 +34,15 @@ RSpec.describe 'Getting issues for an epic' do
NODE NODE
end end
def epic_query(params = {}) def epic_query(params = {}, epic_fields = epic_node)
graphql_query_for("group", { "fullPath" => group.full_path }, graphql_query_for("group", { "fullPath" => group.full_path },
query_graphql_field("epics", params, epic_node) query_graphql_field("epics", params, epic_fields)
) )
end end
def issue_ids def issue_ids(epics = epics_data)
node_array(epics_data).each_with_object({}) do |node, result| node_array(epics).to_h do |node|
result[node['iid'].to_i] = node_array(node['issues']['edges'], 'id') [node['iid'].to_i, node_array(node['issues']['edges'], 'id')]
end end
end end
...@@ -75,13 +75,12 @@ RSpec.describe 'Getting issues for an epic' do ...@@ -75,13 +75,12 @@ RSpec.describe 'Getting issues for an epic' do
end end
context 'pagination' do context 'pagination' do
let(:after_cursor) { '' } def epic_fields(after)
let(:epic_node) do
<<~NODE <<~NODE
edges { edges {
node { node {
iid iid
issues(first: 1, after: "#{after_cursor}") { issues(#{attributes_to_graphql(first: 1, after: after)}) {
pageInfo { pageInfo {
hasNextPage hasNextPage
hasPreviousPage hasPreviousPage
...@@ -99,28 +98,33 @@ RSpec.describe 'Getting issues for an epic' do ...@@ -99,28 +98,33 @@ RSpec.describe 'Getting issues for an epic' do
NODE NODE
end end
context 'without a cursor' do let(:params) { { iid: epic.iid } }
it 'return first page of issues' do
post_graphql(epic_query(iid: epic.iid), current_user: user)
expect(response).to have_gitlab_http_status(:success) def get_page(after)
expect(first_epic_issues_page_info['hasNextPage']).to be_truthy query = epic_query(params, epic_fields(after))
expect(first_epic_issues_page_info['endCursor']).to eq 'MQ' post_graphql(query, current_user: user)
expect(issue_ids[epic.iid]).to eq [issue.to_global_id.to_s]
end expect(response).to have_gitlab_http_status(:success)
data = ::Gitlab::Json.parse(response.body)
expect(data['errors']).to be_blank
epics = data.dig('data', 'group', 'epics', 'edges')
issues = issue_ids(epics)[epic.iid]
page = epics.dig(0, 'node', 'issues', 'pageInfo')
[issues, page]
end end
context 'with an after cursor' do it 'paginates correctly' do
let(:after_cursor) { 'MQ' } issues, page_1 = get_page(nil)
expect(issues).to contain_exactly(global_id_of(issue))
expect(page_1).to include("hasNextPage" => true, "hasPreviousPage" => false)
it 'return first page after the cursor' do next_issues, page_2 = get_page(page_1['endCursor'])
post_graphql(epic_query(iid: epic.iid), current_user: user)
expect(response).to have_gitlab_http_status(:success) expect(next_issues).to contain_exactly(global_id_of(confidential_issue))
expect(first_epic_issues_page_info['hasNextPage']).to be_falsey expect(page_2).to include("hasNextPage" => false, "hasPreviousPage" => true)
expect(first_epic_issues_page_info['endCursor']).to eq 'Mg'
expect(issue_ids[epic.iid]).to eq [confidential_issue.to_global_id.to_s]
end
end end
end end
end end
...@@ -160,7 +164,8 @@ RSpec.describe 'Getting issues for an epic' do ...@@ -160,7 +164,8 @@ RSpec.describe 'Getting issues for an epic' do
# TODO remove the pending state of this spec when # TODO remove the pending state of this spec when
# we have an efficient way of preloading data on GraphQL. # we have an efficient way of preloading data on GraphQL.
# For more information check: https://gitlab.com/gitlab-org/gitlab/-/issues/207898 # For more information check: https://gitlab.com/gitlab-org/gitlab/-/issues/207898
xit 'avoids N+1 queries' do it 'avoids N+1 queries' do
pending 'https://gitlab.com/gitlab-org/gitlab/-/issues/207898'
control_count = ActiveRecord::QueryRecorder.new do control_count = ActiveRecord::QueryRecorder.new do
post_graphql(epic_query(iid: epic.iid), current_user: user) post_graphql(epic_query(iid: epic.iid), current_user: user)
end.count end.count
......
...@@ -9,8 +9,6 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do ...@@ -9,8 +9,6 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do
let_it_be(:other_project) { create(:project) } let_it_be(:other_project) { create(:project) }
let_it_be(:user) { create(:user, security_dashboard_projects: [project]) } let_it_be(:user) { create(:user, security_dashboard_projects: [project]) }
let(:current_user) { user }
before do before do
project.add_developer(user) project.add_developer(user)
other_project.add_developer(user) other_project.add_developer(user)
...@@ -18,28 +16,26 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do ...@@ -18,28 +16,26 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do
stub_licensed_features(security_dashboard: true) stub_licensed_features(security_dashboard: true)
end end
let(:fields) do
<<~QUERY
nodes {
id
}
QUERY
end
let(:query) do let(:query) do
graphql_query_for('instanceSecurityDashboard', nil, query_graphql_field('projects', {}, fields)) graphql_query_for(:instance_security_dashboard, dashboard_fields)
end end
subject(:projects) { graphql_data.dig('instanceSecurityDashboard', 'projects', 'nodes') }
context 'with logged in user' do context 'with logged in user' do
it_behaves_like 'a working graphql query' do let(:current_user) { user }
before do
post_graphql(query, current_user: current_user) context 'requesting projects in the dashboard' do
end let(:dashboard_fields) { query_graphql_path(%i[projects nodes], 'id') }
it 'finds only projects that were added to instance security dashboard' do subject(:projects) { graphql_data_at(:instance_security_dashboard, :projects, :nodes) }
expect(projects).to eq([{ "id" => GitlabSchema.id_from_object(project).to_s }])
it_behaves_like 'a working graphql query' do
before do
post_graphql(query, current_user: current_user)
end
it 'finds only projects that were added to instance security dashboard' do
expect(projects).to contain_exactly({ "id" => global_id_of(project) })
end
end end
end end
...@@ -188,6 +184,10 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do ...@@ -188,6 +184,10 @@ RSpec.describe 'Query.instanceSecurityDashboard.projects' do
context 'with no user' do context 'with no user' do
let(:current_user) { nil } let(:current_user) { nil }
let(:dashboard_fields) { nil }
subject { graphql_data_at(:instance_security_dashboard) }
it_behaves_like 'a working graphql query' do it_behaves_like 'a working graphql query' do
before do before do
post_graphql(query, current_user: current_user) post_graphql(query, current_user: current_user)
......
# frozen_string_literal: true
# A marker interface that allows use to lazily resolve a wider range of value
module Gitlab
module Graphql
module Deferred
def execute
raise NotImplementedError, 'Deferred classes must provide an execute method'
end
end
end
end
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
module Gitlab module Gitlab
module Graphql module Graphql
class ExternallyPaginatedArray < Array class ExternallyPaginatedArray < Array
attr_reader :previous_cursor, :next_cursor attr_reader :start_cursor, :end_cursor
def initialize(previous_cursor, next_cursor, *args) def initialize(previous_cursor, next_cursor, *args)
super(args) super(args)
@previous_cursor = previous_cursor @start_cursor = previous_cursor
@next_cursor = next_cursor @end_cursor = next_cursor
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Graphql
# This module allows your class to easily defer and force values.
# Its methods are just sugar for calls to the Gitlab::Graphql::Lazy class.
#
# example:
#
# class MyAwesomeClass
# def sum_frobbocities(ids)
# ids.map { |n| get_the_thing(n) }.map(&method(:force).sum
# end
#
# def get_the_thing(id)
# thunk = SomeBatchLoader.load(id)
# defer { force(thunk).frobbocity * 2 }
# end
# end
#
# In the example above, we use defer to delay forcing the batch-loaded
# item until we need it, and then we use `force` to consume the lazy values
#
# If `SomeBatchLoader.load(id)` batches correctly, calling
# `sum_frobbocities` will only perform one batched load.
#
module Laziness
def defer(&block)
::Gitlab::Graphql::Lazy.new(&block)
end
def force(lazy)
::Gitlab::Graphql::Lazy.force(lazy)
end
end
end
end
...@@ -24,6 +24,8 @@ module Gitlab ...@@ -24,6 +24,8 @@ module Gitlab
value.force value.force
when ::BatchLoader::GraphQL when ::BatchLoader::GraphQL
value.sync value.sync
when ::Gitlab::Graphql::Deferred
value.execute
when ::GraphQL::Execution::Lazy when ::GraphQL::Execution::Lazy
value.value # part of the private api, but we can force this as well value.value # part of the private api, but we can force this as well
when ::Concurrent::Promise when ::Concurrent::Promise
......
...@@ -8,13 +8,7 @@ module Gitlab ...@@ -8,13 +8,7 @@ module Gitlab
include ::Gitlab::Graphql::ConnectionCollectionMethods include ::Gitlab::Graphql::ConnectionCollectionMethods
prepend ::Gitlab::Graphql::ConnectionRedaction prepend ::Gitlab::Graphql::ConnectionRedaction
def start_cursor delegate :start_cursor, :end_cursor, to: :items
items.previous_cursor
end
def end_cursor
items.next_cursor
end
def next_page? def next_page?
end_cursor.present? end_cursor.present?
......
...@@ -33,7 +33,7 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do ...@@ -33,7 +33,7 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do
end end
context 'finding by status' do context 'finding by status' do
let(:args) { { status: [Types::AlertManagement::StatusEnum.values['IGNORED'].value] } } let(:args) { { statuses: [Types::AlertManagement::StatusEnum.values['IGNORED'].value] } }
it { is_expected.to contain_exactly(ignored_alert) } it { is_expected.to contain_exactly(ignored_alert) }
end end
......
...@@ -29,7 +29,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do ...@@ -29,7 +29,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do
end end
it 'finds only issues matching filters' do it 'finds only issues matching filters' do
result = resolve_board_list_issues(args: { filters: { label_name: label.title, not: { label_name: label2.title } } }).items result = resolve_board_list_issues(args: { filters: { label_name: [label.title], not: { label_name: [label2.title] } } }).items
expect(result).to match_array([issue1, issue3]) expect(result).to match_array([issue1, issue3])
end end
......
...@@ -17,10 +17,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do ...@@ -17,10 +17,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do
describe '#resolve' do describe '#resolve' do
context 'when security_report_types is empty' do context 'when security_report_types is empty' do
it "returns all of the pipeline's jobs" do it "returns all of the pipeline's jobs" do
jobs = resolve(described_class, obj: pipeline, args: {}, ctx: {}) jobs = resolve(described_class, obj: pipeline)
job_names = jobs.map(&:name) expect(jobs).to contain_exactly(
expect(job_names).to contain_exactly('Normal job', 'DAST job', 'SAST job', 'Container scanning job') have_attributes(name: 'Normal job'),
have_attributes(name: 'DAST job'),
have_attributes(name: 'SAST job'),
have_attributes(name: 'Container scanning job')
)
end end
end end
...@@ -30,10 +34,12 @@ RSpec.describe Resolvers::Ci::JobsResolver do ...@@ -30,10 +34,12 @@ RSpec.describe Resolvers::Ci::JobsResolver do
::Types::Security::ReportTypeEnum.values['SAST'].value, ::Types::Security::ReportTypeEnum.values['SAST'].value,
::Types::Security::ReportTypeEnum.values['DAST'].value ::Types::Security::ReportTypeEnum.values['DAST'].value
] ]
jobs = resolve(described_class, obj: pipeline, args: { security_report_types: report_types }, ctx: {}) jobs = resolve(described_class, obj: pipeline, args: { security_report_types: report_types })
job_names = jobs.map(&:name) expect(jobs).to contain_exactly(
expect(job_names).to contain_exactly('DAST job', 'SAST job') have_attributes(name: 'DAST job'),
have_attributes(name: 'SAST job')
)
end end
end end
end end
......
...@@ -50,6 +50,6 @@ RSpec.describe Resolvers::CommitPipelinesResolver do ...@@ -50,6 +50,6 @@ RSpec.describe Resolvers::CommitPipelinesResolver do
it 'resolves pipelines for commit and ref' do it 'resolves pipelines for commit and ref' do
pipelines = resolve_pipelines pipelines = resolve_pipelines
expect(pipelines).to eq([pipeline2, pipeline]) expect(pipelines.to_a).to eq([pipeline2, pipeline])
end end
end end
...@@ -5,17 +5,23 @@ require 'spec_helper' ...@@ -5,17 +5,23 @@ require 'spec_helper'
RSpec.describe ::CachingArrayResolver do RSpec.describe ::CachingArrayResolver do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:non_admins) { create_list(:user, 4, admin: false) } let_it_be(:admins) { create_list(:user, 4, admin: true) }
let(:query_context) { {} } let(:query_context) { { current_user: admins.first } }
let(:max_page_size) { 10 } let(:max_page_size) { 10 }
let(:field) { double('Field', max_page_size: max_page_size) } let(:field) { double('Field', max_page_size: max_page_size) }
let(:schema) { double('Schema', default_max_page_size: 3) } let(:schema) do
Class.new(GitlabSchema) do
default_max_page_size 3
end
end
let_it_be(:caching_resolver) do let_it_be(:caching_resolver) do
mod = described_class mod = described_class
Class.new(::Resolvers::BaseResolver) do Class.new(::Resolvers::BaseResolver) do
include mod include mod
type [::Types::UserType], null: true
argument :is_admin, ::GraphQL::BOOLEAN_TYPE, required: false
def query_input(is_admin:) def query_input(is_admin:)
is_admin is_admin
...@@ -44,6 +50,8 @@ RSpec.describe ::CachingArrayResolver do ...@@ -44,6 +50,8 @@ RSpec.describe ::CachingArrayResolver do
Class.new(::Resolvers::BaseResolver) do Class.new(::Resolvers::BaseResolver) do
include mod include mod
type [::Types::UserType], null: true
argument :username, ::GraphQL::STRING_TYPE, required: false
def query_input(username:) def query_input(username:)
username username
...@@ -72,7 +80,7 @@ RSpec.describe ::CachingArrayResolver do ...@@ -72,7 +80,7 @@ RSpec.describe ::CachingArrayResolver do
expect(User).to receive(:from_union).twice.and_call_original expect(User).to receive(:from_union).twice.and_call_original
results = users.in_groups_of(2, false).map do |users| results = users.in_groups_of(2, false).map do |users|
resolve(resolver, args: { username: users.map(&:username) }, field: field, schema: schema) resolve(resolver, args: { username: users.map(&:username) }, schema: schema)
end end
expect(results.flat_map(&method(:force))).to match_array(users) expect(results.flat_map(&method(:force))).to match_array(users)
...@@ -80,7 +88,7 @@ RSpec.describe ::CachingArrayResolver do ...@@ -80,7 +88,7 @@ RSpec.describe ::CachingArrayResolver do
end end
context 'all queries return results' do context 'all queries return results' do
let_it_be(:admins) { create_list(:admin, 3) } let_it_be(:non_admins) { create_list(:user, 3, admin: false) }
it 'batches the queries' do it 'batches the queries' do
expect do expect do
...@@ -113,9 +121,9 @@ RSpec.describe ::CachingArrayResolver do ...@@ -113,9 +121,9 @@ RSpec.describe ::CachingArrayResolver do
found_others = resolve_users(false) found_others = resolve_users(false)
found_all = resolve_users(nil) found_all = resolve_users(nil)
expect(force(found_admins)).to be_empty expect(force(found_admins)).to match_array(admins)
expect(force(found_others)).to match_array(non_admins) expect(force(found_others)).to be_empty
expect(force(found_all)).to match_array(non_admins) expect(force(found_all)).to match_array(admins)
end end
end end
...@@ -128,13 +136,13 @@ RSpec.describe ::CachingArrayResolver do ...@@ -128,13 +136,13 @@ RSpec.describe ::CachingArrayResolver do
expect do expect do
repeated_find = resolve_users(nil) repeated_find = resolve_users(nil)
expect(force(repeated_find)).to match_array(non_admins) expect(force(repeated_find)).to match_array(admins)
end.not_to exceed_query_limit(0) end.not_to exceed_query_limit(0)
end end
end end
context 'the resolver overrides item_found' do context 'the resolver overrides item_found' do
let_it_be(:admins) { create_list(:admin, 2) } let_it_be(:non_admins) { create_list(:user, 2, admin: false) }
let(:query_context) do let(:query_context) do
{ {
found: { true => [], false => [], nil => [] } found: { true => [], false => [], nil => [] }
...@@ -156,8 +164,8 @@ RSpec.describe ::CachingArrayResolver do ...@@ -156,8 +164,8 @@ RSpec.describe ::CachingArrayResolver do
[found_admins, found_all].each(&method(:force)) [found_admins, found_all].each(&method(:force))
expect(query_context[:found]).to match({ expect(query_context[:found]).to match({
false => be_empty,
true => match_array(admins), true => match_array(admins),
false => be_empty,
nil => match_array(admins + non_admins) nil => match_array(admins + non_admins)
}) })
end end
...@@ -168,10 +176,10 @@ RSpec.describe ::CachingArrayResolver do ...@@ -168,10 +176,10 @@ RSpec.describe ::CachingArrayResolver do
it 'respects the max_page_size, on a per subset basis' do it 'respects the max_page_size, on a per subset basis' do
found_all = resolve_users(nil) found_all = resolve_users(nil)
found_others = resolve_users(false) found_admins = resolve_users(true)
expect(force(found_all).size).to eq(2) expect(force(found_all).size).to eq(2)
expect(force(found_others).size).to eq(2) expect(force(found_admins).size).to eq(2)
end end
end end
...@@ -180,10 +188,10 @@ RSpec.describe ::CachingArrayResolver do ...@@ -180,10 +188,10 @@ RSpec.describe ::CachingArrayResolver do
it 'takes the page size from schema.default_max_page_size' do it 'takes the page size from schema.default_max_page_size' do
found_all = resolve_users(nil) found_all = resolve_users(nil)
found_others = resolve_users(false) found_admins = resolve_users(true)
expect(force(found_all).size).to eq(schema.default_max_page_size) expect(force(found_all).size).to eq(schema.default_max_page_size)
expect(force(found_others).size).to eq(schema.default_max_page_size) expect(force(found_admins).size).to eq(schema.default_max_page_size)
end end
end end
...@@ -199,7 +207,9 @@ RSpec.describe ::CachingArrayResolver do ...@@ -199,7 +207,9 @@ RSpec.describe ::CachingArrayResolver do
def resolve_users(is_admin, resolver = caching_resolver) def resolve_users(is_admin, resolver = caching_resolver)
args = { is_admin: is_admin } args = { is_admin: is_admin }
resolve(resolver, args: args, field: field, ctx: query_context, schema: schema) opts = resolver.field_options
allow(resolver).to receive(:field_options).and_return(opts.merge(max_page_size: max_page_size))
resolve(resolver, args: args, ctx: query_context, schema: schema, field: field)
end end
def force(lazy) def force(lazy)
......
...@@ -15,7 +15,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do ...@@ -15,7 +15,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
let(:all_singular_args) do let(:all_singular_args) do
{ {
design_at_version_id: global_id_of(dav(design)), id: global_id_of(dav(design)),
design_id: global_id_of(design), design_id: global_id_of(design),
filename: design.filename filename: design.filename
} }
...@@ -50,7 +50,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do ...@@ -50,7 +50,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
end end
end end
%i[design_at_version_id design_id filename].each do |arg| %i[id design_id filename].each do |arg|
describe "passing #{arg}" do describe "passing #{arg}" do
let(:args) { all_singular_args.slice(arg) } let(:args) { all_singular_args.slice(arg) }
...@@ -71,7 +71,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do ...@@ -71,7 +71,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do
describe 'attempting to retrieve an object not visible at this version' do describe 'attempting to retrieve an object not visible at this version' do
let(:design) { design_d } let(:design) { design_d }
%i[design_at_version_id design_id filename].each do |arg| %i[id design_id filename].each do |arg|
describe "passing #{arg}" do describe "passing #{arg}" do
let(:args) { all_singular_args.slice(arg) } let(:args) { all_singular_args.slice(arg) }
......
...@@ -32,7 +32,7 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do ...@@ -32,7 +32,7 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do
end end
context 'we pass an id' do context 'we pass an id' do
let(:params) { { version_id: global_id_of(first_version) } } let(:params) { { id: global_id_of(first_version) } }
it { is_expected.to eq(first_version) } it { is_expected.to eq(first_version) }
end end
...@@ -44,13 +44,13 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do ...@@ -44,13 +44,13 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do
end end
context 'we pass an inconsistent mixture of sha and version id' do context 'we pass an inconsistent mixture of sha and version id' do
let(:params) { { sha: first_version.sha, version_id: global_id_of(create(:design_version)) } } let(:params) { { sha: first_version.sha, id: global_id_of(create(:design_version)) } }
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
context 'we pass the id of something that is not a design_version' do context 'we pass the id of something that is not a design_version' do
let(:params) { { version_id: global_id_of(project) } } let(:params) { { id: global_id_of(project) } }
let(:appropriate_error) { ::GraphQL::CoercionError } let(:appropriate_error) { ::GraphQL::CoercionError }
it 'raises an appropriate error' do it 'raises an appropriate error' do
......
...@@ -27,7 +27,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -27,7 +27,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end end
shared_examples 'a source of versions' do shared_examples 'a source of versions' do
subject(:result) { resolve_versions(object) } subject(:result) { resolve_versions(object)&.to_a }
let_it_be(:all_versions) { object.versions.ordered } let_it_be(:all_versions) { object.versions.ordered }
...@@ -39,7 +39,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -39,7 +39,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
context 'without constraints' do context 'without constraints' do
it 'returns the ordered versions' do it 'returns the ordered versions' do
expect(result).to eq(all_versions) expect(result.to_a).to eq(all_versions)
end end
end end
...@@ -51,13 +51,13 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -51,13 +51,13 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end end
context 'by earlier_or_equal_to_id' do context 'by earlier_or_equal_to_id' do
let(:params) { { id: global_id_of(first_version) } } let(:params) { { earlier_or_equal_to_id: global_id_of(first_version) } }
it_behaves_like 'a query for all_versions up to the first_version' it_behaves_like 'a query for all_versions up to the first_version'
end end
context 'by earlier_or_equal_to_sha' do context 'by earlier_or_equal_to_sha' do
let(:params) { { sha: first_version.sha } } let(:params) { { earlier_or_equal_to_sha: first_version.sha } }
it_behaves_like 'a query for all_versions up to the first_version' it_behaves_like 'a query for all_versions up to the first_version'
end end
...@@ -68,8 +68,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -68,8 +68,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
# return successfully # return successfully
let(:params) do let(:params) do
{ {
sha: first_version.sha, earlier_or_equal_to_sha: first_version.sha,
id: global_id_of(first_version) earlier_or_equal_to_id: global_id_of(first_version)
} }
end end
...@@ -79,8 +79,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -79,8 +79,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
context 'and they do not match' do context 'and they do not match' do
let(:params) do let(:params) do
{ {
sha: first_version.sha, earlier_or_equal_to_sha: first_version.sha,
id: global_id_of(other_version) earlier_or_equal_to_id: global_id_of(other_version)
} }
end end
...@@ -111,7 +111,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do ...@@ -111,7 +111,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
end end
def resolve_versions(obj, context = { current_user: current_user }) def resolve_versions(obj, context = { current_user: current_user })
eager_resolve(resolver, obj: obj, args: params.merge(parent: parent), ctx: context) eager_resolve(resolver, obj: obj, parent: parent, args: params, ctx: context)
end end
end end
end end
...@@ -88,8 +88,8 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do ...@@ -88,8 +88,8 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do
it 'sets the pagination variables' do it 'sets the pagination variables' do
result = resolve_errors result = resolve_errors
expect(result.next_cursor).to eq 'next' expect(result.end_cursor).to eq 'next'
expect(result.previous_cursor).to eq 'prev' expect(result.start_cursor).to eq 'prev'
end end
it 'returns an externally paginated array' do it 'returns an externally paginated array' do
......
...@@ -62,22 +62,13 @@ RSpec.describe Resolvers::IssueStatusCountsResolver do ...@@ -62,22 +62,13 @@ RSpec.describe Resolvers::IssueStatusCountsResolver do
end end
it 'filters by issue type', :aggregate_failures do it 'filters by issue type', :aggregate_failures do
result = resolve_issue_status_counts(issue_types: ['incident']) result = resolve_issue_status_counts(types: ['incident'])
expect(result.all).to eq 1 expect(result.all).to eq 1
expect(result.opened).to eq 0 expect(result.opened).to eq 0
expect(result.closed).to eq 1 expect(result.closed).to eq 1
end end
# The state param is ignored in IssuableFinder#count_by_state
it 'ignores state filter', :aggregate_failures do
result = resolve_issue_status_counts(state: 'closed')
expect(result.all).to eq 2
expect(result.opened).to eq 1
expect(result.closed).to eq 1
end
private private
def resolve_issue_status_counts(args = {}, context = { current_user: current_user }) def resolve_issue_status_counts(args = {}, context = { current_user: current_user })
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Resolvers::IssuesResolver do RSpec.describe Resolvers::IssuesResolver do
include GraphqlHelpers include GraphqlHelpers
let(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
...@@ -25,7 +25,7 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -25,7 +25,7 @@ RSpec.describe Resolvers::IssuesResolver do
end end
context "with a project" do context "with a project" do
before do before_all do
project.add_developer(current_user) project.add_developer(current_user)
create(:label_link, label: label1, target: issue1) create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2) create(:label_link, label: label1, target: issue2)
...@@ -43,11 +43,11 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -43,11 +43,11 @@ RSpec.describe Resolvers::IssuesResolver do
end end
it 'filters by milestone' do it 'filters by milestone' do
expect(resolve_issues(milestone_title: milestone.title)).to contain_exactly(issue1) expect(resolve_issues(milestone_title: [milestone.title])).to contain_exactly(issue1)
end end
it 'filters by assignee_username' do it 'filters by assignee_username' do
expect(resolve_issues(assignee_username: assignee.username)).to contain_exactly(issue2) expect(resolve_issues(assignee_username: [assignee.username])).to contain_exactly(issue2)
end end
it 'filters by two assignees' do it 'filters by two assignees' do
...@@ -112,15 +112,19 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -112,15 +112,19 @@ RSpec.describe Resolvers::IssuesResolver do
describe 'filters by issue_type' do describe 'filters by issue_type' do
it 'filters by a single type' do it 'filters by a single type' do
expect(resolve_issues(issue_types: ['incident'])).to contain_exactly(issue1) expect(resolve_issues(types: %w[incident])).to contain_exactly(issue1)
end
it 'filters by a single type, negative assertion' do
expect(resolve_issues(types: %w[issue])).not_to include(issue1)
end end
it 'filters by more than one type' do it 'filters by more than one type' do
expect(resolve_issues(issue_types: %w(incident issue))).to contain_exactly(issue1, issue2) expect(resolve_issues(types: %w[incident issue])).to contain_exactly(issue1, issue2)
end end
it 'ignores the filter if none given' do it 'ignores the filter if none given' do
expect(resolve_issues(issue_types: [])).to contain_exactly(issue1, issue2) expect(resolve_issues(types: [])).to contain_exactly(issue1, issue2)
end end
end end
...@@ -143,44 +147,44 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -143,44 +147,44 @@ RSpec.describe Resolvers::IssuesResolver do
describe 'sorting' do describe 'sorting' do
context 'when sorting by created' do context 'when sorting by created' do
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: 'created_asc')).to eq [issue1, issue2] expect(resolve_issues(sort: 'created_asc').to_a).to eq [issue1, issue2]
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1] expect(resolve_issues(sort: 'created_desc').to_a).to eq [issue2, issue1]
end end
end end
context 'when sorting by due date' do context 'when sorting by due date' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:due_issue1) { create(:issue, project: project, due_date: 3.days.from_now) } let_it_be(:due_issue1) { create(:issue, project: project, due_date: 3.days.from_now) }
let_it_be(:due_issue2) { create(:issue, project: project, due_date: nil) } let_it_be(:due_issue2) { create(:issue, project: project, due_date: nil) }
let_it_be(:due_issue3) { create(:issue, project: project, due_date: 2.days.ago) } let_it_be(:due_issue3) { create(:issue, project: project, due_date: 2.days.ago) }
let_it_be(:due_issue4) { create(:issue, project: project, due_date: nil) } let_it_be(:due_issue4) { create(:issue, project: project, due_date: nil) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :due_date_asc)).to eq [due_issue3, due_issue1, due_issue4, due_issue2] expect(resolve_issues(sort: :due_date_asc).to_a).to eq [due_issue3, due_issue1, due_issue4, due_issue2]
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :due_date_desc)).to eq [due_issue1, due_issue3, due_issue4, due_issue2] expect(resolve_issues(sort: :due_date_desc).to_a).to eq [due_issue1, due_issue3, due_issue4, due_issue2]
end end
end end
context 'when sorting by relative position' do context 'when sorting by relative position' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:relative_issue1) { create(:issue, project: project, relative_position: 2000) } let_it_be(:relative_issue1) { create(:issue, project: project, relative_position: 2000) }
let_it_be(:relative_issue2) { create(:issue, project: project, relative_position: nil) } let_it_be(:relative_issue2) { create(:issue, project: project, relative_position: nil) }
let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) } let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) }
let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) } let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :relative_position_asc)).to eq [relative_issue3, relative_issue1, relative_issue4, relative_issue2] expect(resolve_issues(sort: :relative_position_asc).to_a).to eq [relative_issue3, relative_issue1, relative_issue4, relative_issue2]
end end
end end
context 'when sorting by priority' do context 'when sorting by priority' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
let_it_be(:priority_label1) { create(:label, project: project, priority: 1) } let_it_be(:priority_label1) { create(:label, project: project, priority: 1) }
...@@ -200,7 +204,7 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -200,7 +204,7 @@ RSpec.describe Resolvers::IssuesResolver do
end end
context 'when sorting by label priority' do context 'when sorting by label priority' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:label1) { create(:label, project: project, priority: 1) } let_it_be(:label1) { create(:label, project: project, priority: 1) }
let_it_be(:label2) { create(:label, project: project, priority: 5) } let_it_be(:label2) { create(:label, project: project, priority: 5) }
let_it_be(:label3) { create(:label, project: project, priority: 10) } let_it_be(:label3) { create(:label, project: project, priority: 10) }
...@@ -219,7 +223,7 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -219,7 +223,7 @@ RSpec.describe Resolvers::IssuesResolver do
end end
context 'when sorting by milestone due date' do context 'when sorting by milestone due date' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) }
let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) }
let_it_be(:milestone_issue1) { create(:issue, project: project) } let_it_be(:milestone_issue1) { create(:issue, project: project) }
...@@ -236,17 +240,17 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -236,17 +240,17 @@ RSpec.describe Resolvers::IssuesResolver do
end end
context 'when sorting by severity' do context 'when sorting by severity' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :public) }
let_it_be(:issue_high_severity) { create_issue_with_severity(project, severity: :high) } let_it_be(:issue_high_severity) { create_issue_with_severity(project, severity: :high) }
let_it_be(:issue_low_severity) { create_issue_with_severity(project, severity: :low) } let_it_be(:issue_low_severity) { create_issue_with_severity(project, severity: :low) }
let_it_be(:issue_no_severity) { create(:incident, project: project) } let_it_be(:issue_no_severity) { create(:incident, project: project) }
it 'sorts issues ascending' do it 'sorts issues ascending' do
expect(resolve_issues(sort: :severity_asc)).to eq([issue_no_severity, issue_low_severity, issue_high_severity]) expect(resolve_issues(sort: :severity_asc).to_a).to eq([issue_no_severity, issue_low_severity, issue_high_severity])
end end
it 'sorts issues descending' do it 'sorts issues descending' do
expect(resolve_issues(sort: :severity_desc)).to eq([issue_high_severity, issue_low_severity, issue_no_severity]) expect(resolve_issues(sort: :severity_desc).to_a).to eq([issue_high_severity, issue_low_severity, issue_no_severity])
end end
end end
end end
...@@ -267,7 +271,9 @@ RSpec.describe Resolvers::IssuesResolver do ...@@ -267,7 +271,9 @@ RSpec.describe Resolvers::IssuesResolver do
it 'batches queries that only include IIDs', :request_store do it 'batches queries that only include IIDs', :request_store do
result = batch_sync(max_queries: 2) do result = batch_sync(max_queries: 2) do
resolve_issues(iid: issue1.iid) + resolve_issues(iids: issue2.iid) [issue1, issue2]
.map { |issue| resolve_issues(iid: issue.iid.to_s) }
.flat_map(&:to_a)
end end
expect(result).to contain_exactly(issue1, issue2) expect(result).to contain_exactly(issue1, issue2)
......
...@@ -75,9 +75,9 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -75,9 +75,9 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'can batch-resolve merge requests from different projects' do it 'can batch-resolve merge requests from different projects' do
# 2 queries for project_authorizations, and 2 for merge_requests # 2 queries for project_authorizations, and 2 for merge_requests
result = batch_sync(max_queries: queries_per_project * 2) do result = batch_sync(max_queries: queries_per_project * 2) do
resolve_mr(project, iids: iid_1) + resolve_mr(project, iids: [iid_1]) +
resolve_mr(project, iids: iid_2) + resolve_mr(project, iids: [iid_2]) +
resolve_mr(other_project, iids: other_iid) resolve_mr(other_project, iids: [other_iid])
end end
expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request)
...@@ -110,7 +110,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -110,7 +110,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
context 'by source branches' do context 'by source branches' do
it 'takes one argument' do it 'takes one argument' do
result = resolve_mr(project, source_branch: [merge_request_3.source_branch]) result = resolve_mr(project, source_branches: [merge_request_3.source_branch])
expect(result).to contain_exactly(merge_request_3) expect(result).to contain_exactly(merge_request_3)
end end
...@@ -118,7 +118,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -118,7 +118,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'takes more than one argument' do it 'takes more than one argument' do
mrs = [merge_request_3, merge_request_4] mrs = [merge_request_3, merge_request_4]
branches = mrs.map(&:source_branch) branches = mrs.map(&:source_branch)
result = resolve_mr(project, source_branch: branches ) result = resolve_mr(project, source_branches: branches )
expect(result).to match_array(mrs) expect(result).to match_array(mrs)
end end
...@@ -126,7 +126,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -126,7 +126,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
context 'by target branches' do context 'by target branches' do
it 'takes one argument' do it 'takes one argument' do
result = resolve_mr(project, target_branch: [merge_request_3.target_branch]) result = resolve_mr(project, target_branches: [merge_request_3.target_branch])
expect(result).to contain_exactly(merge_request_3) expect(result).to contain_exactly(merge_request_3)
end end
...@@ -134,7 +134,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -134,7 +134,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'takes more than one argument' do it 'takes more than one argument' do
mrs = [merge_request_3, merge_request_4] mrs = [merge_request_3, merge_request_4]
branches = mrs.map(&:target_branch) branches = mrs.map(&:target_branch)
result = resolve_mr(project, target_branch: branches ) result = resolve_mr(project, target_branches: branches )
expect(result.compact).to match_array(mrs) expect(result.compact).to match_array(mrs)
end end
...@@ -153,13 +153,13 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -153,13 +153,13 @@ RSpec.describe Resolvers::MergeRequestsResolver do
let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) } let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) }
it 'takes one argument' do it 'takes one argument' do
result = resolve_mr(project, label_name: [label.title]) result = resolve_mr(project, labels: [label.title])
expect(result).to contain_exactly(merge_request_6, with_label) expect(result).to contain_exactly(merge_request_6, with_label)
end end
it 'takes multiple arguments, with semantics of ALL MUST MATCH' do it 'takes multiple arguments, with semantics of ALL MUST MATCH' do
result = resolve_mr(project, label_name: merge_request_6.labels.map(&:title)) result = resolve_mr(project, labels: merge_request_6.labels.map(&:title))
expect(result).to contain_exactly(merge_request_6) expect(result).to contain_exactly(merge_request_6)
end end
...@@ -201,7 +201,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -201,7 +201,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'requires all filters' do it 'requires all filters' do
create(:merge_request, :closed, source_project: project, target_project: project, source_branch: merge_request_4.source_branch) create(:merge_request, :closed, source_project: project, target_project: project, source_branch: merge_request_4.source_branch)
result = resolve_mr(project, source_branch: [merge_request_4.source_branch], state: 'locked') result = resolve_mr(project, source_branches: [merge_request_4.source_branch], state: 'locked')
expect(result.compact).to contain_exactly(merge_request_4) expect(result.compact).to contain_exactly(merge_request_4)
end end
...@@ -236,7 +236,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do ...@@ -236,7 +236,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end end
def resolve_mr_single(project, iid) def resolve_mr_single(project, iid)
resolve_mr(project, resolver: described_class.single, iids: iid) resolve_mr(project, resolver: described_class.single, iid: iid.to_s)
end end
def resolve_mr(project, resolver: described_class, user: current_user, **args) def resolve_mr(project, resolver: described_class, user: current_user, **args)
......
...@@ -28,7 +28,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do ...@@ -28,7 +28,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
it 'filters merge requests by assignee username' do it 'filters merge requests by assignee username' do
result = resolve_mr(project, assignee_username: other_user.username) result = resolve_mr(project, assignee_username: other_user.username)
expect(result).to eq([merge_request]) expect(result).to contain_exactly(merge_request)
end end
it 'does not find anything' do it 'does not find anything' do
...@@ -42,7 +42,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do ...@@ -42,7 +42,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
it 'filters merge requests by author username' do it 'filters merge requests by author username' do
result = resolve_mr(project, author_username: other_user.username) result = resolve_mr(project, author_username: other_user.username)
expect(result).to eq([merge_request]) expect(result).to contain_exactly(merge_request)
end end
it 'does not find anything' do it 'does not find anything' do
...@@ -56,7 +56,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do ...@@ -56,7 +56,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do
it 'filters merge requests by reviewer username' do it 'filters merge requests by reviewer username' do
result = resolve_mr(project, reviewer_username: reviewer.username) result = resolve_mr(project, reviewer_username: reviewer.username)
expect(result).to eq([merge_request]) expect(result).to contain_exactly(merge_request)
end end
it 'does not find anything' do it 'does not find anything' do
......
...@@ -6,16 +6,18 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do ...@@ -6,16 +6,18 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
include GraphqlHelpers include GraphqlHelpers
describe '#resolve' do describe '#resolve' do
let_it_be(:current_user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:other_user) { create(:user) } let_it_be(:other_user) { create(:user) }
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: current_user) } let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: user) }
let_it_be(:project_snippet) { create(:project_snippet, :internal, author: current_user, project: project) } let_it_be(:project_snippet) { create(:project_snippet, :internal, author: user, project: project) }
let_it_be(:other_project_snippet) { create(:project_snippet, :public, author: other_user, project: project) } let_it_be(:other_project_snippet) { create(:project_snippet, :public, author: other_user, project: project) }
before do let(:current_user) { user }
project.add_developer(current_user)
before_all do
project.add_developer(user)
end end
it 'calls SnippetsFinder' do it 'calls SnippetsFinder' do
...@@ -42,20 +44,26 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do ...@@ -42,20 +44,26 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
end end
it 'returns the snippets by gid' do it 'returns the snippets by gid' do
snippets = resolve_snippets(args: { ids: project_snippet.to_global_id }) snippets = resolve_snippets(args: { ids: [global_id_of(project_snippet)] })
expect(snippets).to contain_exactly(project_snippet) expect(snippets).to contain_exactly(project_snippet)
end end
it 'returns the snippets by array of gid' do it 'returns the snippets by array of gid' do
args = { args = {
ids: [project_snippet.to_global_id, other_project_snippet.to_global_id] ids: [global_id_of(project_snippet), global_id_of(other_project_snippet)]
} }
snippets = resolve_snippets(args: args) snippets = resolve_snippets(args: args)
expect(snippets).to contain_exactly(project_snippet, other_project_snippet) expect(snippets).to contain_exactly(project_snippet, other_project_snippet)
end end
it 'returns an error if the gid is invalid' do
expect do
resolve_snippets(args: { ids: ['foo'] })
end.to raise_error(GraphQL::CoercionError)
end
end end
context 'when no project is provided' do context 'when no project is provided' do
...@@ -65,8 +73,10 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do ...@@ -65,8 +73,10 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do
end end
context 'when provided user is not current user' do context 'when provided user is not current user' do
let(:current_user) { other_user }
it 'returns no snippets' do it 'returns no snippets' do
expect(resolve_snippets(context: { current_user: other_user }, args: { ids: project_snippet.to_global_id })).to be_empty expect(resolve_snippets(args: { ids: [global_id_of(project_snippet)] })).to be_empty
end end
end end
......
...@@ -17,6 +17,7 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -17,6 +17,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let_it_be(:public_user) { create(:user) } let_it_be(:public_user) { create(:user) }
let(:args) { { sort: :released_at_desc } } let(:args) { { sort: :released_at_desc } }
let(:all_releases) { [release_v1, release_v2, release_v3] }
before do before do
project.add_developer(developer) project.add_developer(developer)
...@@ -27,7 +28,7 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -27,7 +28,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:current_user) { public_user } let(:current_user) { public_user }
it 'returns an empty array' do it 'returns an empty array' do
expect(resolve_releases).to eq([]) expect(resolve_releases).to be_empty
end end
end end
...@@ -35,7 +36,7 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -35,7 +36,7 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:current_user) { developer } let(:current_user) { developer }
it 'returns all releases associated to the project' do it 'returns all releases associated to the project' do
expect(resolve_releases).to eq([release_v3, release_v2, release_v1]) expect(resolve_releases).to match_array(all_releases)
end end
describe 'sorting behavior' do describe 'sorting behavior' do
...@@ -43,7 +44,9 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -43,7 +44,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :released_at_desc } } let(:args) { { sort: :released_at_desc } }
it 'returns the releases ordered by released_at in descending order' do it 'returns the releases ordered by released_at in descending order' do
expect(resolve_releases).to eq([release_v3, release_v2, release_v1]) expect(resolve_releases.to_a)
.to match_array(all_releases)
.and be_sorted(:released_at, :desc)
end end
end end
...@@ -51,7 +54,9 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -51,7 +54,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :released_at_asc } } let(:args) { { sort: :released_at_asc } }
it 'returns the releases ordered by released_at in ascending order' do it 'returns the releases ordered by released_at in ascending order' do
expect(resolve_releases).to eq([release_v1, release_v2, release_v3]) expect(resolve_releases.to_a)
.to match_array(all_releases)
.and be_sorted(:released_at, :asc)
end end
end end
...@@ -59,7 +64,9 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -59,7 +64,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :created_desc } } let(:args) { { sort: :created_desc } }
it 'returns the releases ordered by created_at in descending order' do it 'returns the releases ordered by created_at in descending order' do
expect(resolve_releases).to eq([release_v1, release_v3, release_v2]) expect(resolve_releases.to_a)
.to match_array(all_releases)
.and be_sorted(:created_at, :desc)
end end
end end
...@@ -67,7 +74,9 @@ RSpec.describe Resolvers::ReleasesResolver do ...@@ -67,7 +74,9 @@ RSpec.describe Resolvers::ReleasesResolver do
let(:args) { { sort: :created_asc } } let(:args) { { sort: :created_asc } }
it 'returns the releases ordered by created_at in ascending order' do it 'returns the releases ordered by created_at in ascending order' do
expect(resolve_releases).to eq([release_v2, release_v3, release_v1]) expect(resolve_releases.to_a)
.to match_array(all_releases)
.and be_sorted(:created_at, :asc)
end end
end end
end end
......
...@@ -84,19 +84,18 @@ RSpec.describe Resolvers::SnippetsResolver do ...@@ -84,19 +84,18 @@ RSpec.describe Resolvers::SnippetsResolver do
end end
it 'returns the snippets by single gid' do it 'returns the snippets by single gid' do
snippets = resolve_snippets(args: { ids: personal_snippet.to_global_id }) snippets = resolve_snippets(args: { ids: [global_id_of(personal_snippet)] })
expect(snippets).to contain_exactly(personal_snippet) expect(snippets).to contain_exactly(personal_snippet)
end end
it 'returns the snippets by array of gid' do it 'returns the snippets by array of gid' do
args = { snippets = [personal_snippet, project_snippet]
ids: [personal_snippet.to_global_id, project_snippet.to_global_id] args = { ids: snippets.map { |s| global_id_of(s) } }
}
snippets = resolve_snippets(args: args) found = resolve_snippets(args: args)
expect(snippets).to contain_exactly(personal_snippet, project_snippet) expect(found).to match_array(snippets)
end end
it 'returns an error if the id cannot be coerced' do it 'returns an error if the id cannot be coerced' do
......
...@@ -20,7 +20,7 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -20,7 +20,7 @@ RSpec.describe Resolvers::TodoResolver do
it 'calls TodosFinder' do it 'calls TodosFinder' do
expect_next_instance_of(TodosFinder) do |finder| expect_next_instance_of(TodosFinder) do |finder|
expect(finder).to receive(:execute) expect(finder).to receive(:execute).and_call_original
end end
resolve_todos resolve_todos
...@@ -48,7 +48,7 @@ RSpec.describe Resolvers::TodoResolver do ...@@ -48,7 +48,7 @@ RSpec.describe Resolvers::TodoResolver do
end end
it 'returns the todos for single filter' do it 'returns the todos for single filter' do
todos = resolve_todos(type: 'MergeRequest') todos = resolve_todos(type: ['MergeRequest'])
expect(todos).to contain_exactly(merge_request_todo_pending) expect(todos).to contain_exactly(merge_request_todo_pending)
end end
......
...@@ -51,24 +51,23 @@ RSpec.describe Resolvers::Users::SnippetsResolver do ...@@ -51,24 +51,23 @@ RSpec.describe Resolvers::Users::SnippetsResolver do
end end
it 'returns the snippets by single gid' do it 'returns the snippets by single gid' do
snippets = resolve_snippets(args: { ids: private_personal_snippet.to_global_id }) snippets = resolve_snippets(args: { ids: [global_id_of(private_personal_snippet)] })
expect(snippets).to contain_exactly(private_personal_snippet) expect(snippets).to contain_exactly(private_personal_snippet)
end end
it 'returns the snippets by array of gid' do it 'returns the snippets by array of gid' do
args = { snippets = [private_personal_snippet, public_personal_snippet]
ids: [private_personal_snippet.to_global_id, public_personal_snippet.to_global_id] args = { ids: snippets.map { |s| global_id_of(s) } }
}
snippets = resolve_snippets(args: args) found = resolve_snippets(args: args)
expect(snippets).to contain_exactly(private_personal_snippet, public_personal_snippet) expect(found).to match_array(snippets)
end end
it 'returns an error if the gid is invalid' do it 'returns an error if the gid is invalid' do
args = { args = {
ids: [private_personal_snippet.to_global_id, 'foo'] ids: [global_id_of(private_personal_snippet), 'foo']
} }
expect do expect do
......
...@@ -11,11 +11,14 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do ...@@ -11,11 +11,14 @@ RSpec.describe GitlabSchema.types['AlertManagementPrometheusIntegration'] do
describe 'resolvers' do describe 'resolvers' do
shared_examples_for 'has field with value' do |field_name| shared_examples_for 'has field with value' do |field_name|
it 'correctly renders the field' do it 'correctly renders the field' do
expect(resolve_field(field_name, integration)).to eq(value) result = resolve_field(field_name, integration, current_user: user)
expect(result).to eq(value)
end end
end end
let_it_be_with_reload(:integration) { create(:prometheus_service) } let_it_be_with_reload(:integration) { create(:prometheus_service) }
let_it_be(:user) { create(:user, maintainer_projects: [integration.project]) }
it_behaves_like 'has field with value', 'name' do it_behaves_like 'has field with value', 'name' do
let(:value) { integration.title } let(:value) { integration.title }
......
...@@ -6,8 +6,9 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do ...@@ -6,8 +6,9 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:admin) { create(:admin) } let_it_be(:admin) { create(:admin) }
let(:queue) { 'authorized_projects' }
let(:variables) { { user: admin.username, queue_name: 'authorized_projects' } } let(:variables) { { user: admin.username, queue_name: queue } }
let(:mutation) { graphql_mutation(:admin_sidekiq_queues_delete_jobs, variables) } let(:mutation) { graphql_mutation(:admin_sidekiq_queues_delete_jobs, variables) }
def mutation_response def mutation_response
...@@ -26,18 +27,19 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do ...@@ -26,18 +27,19 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
context 'valid request' do context 'valid request' do
around do |example| around do |example|
Sidekiq::Queue.new('authorized_projects').clear Sidekiq::Queue.new(queue).clear
Sidekiq::Testing.disable!(&example) Sidekiq::Testing.disable!(&example)
Sidekiq::Queue.new('authorized_projects').clear Sidekiq::Queue.new(queue).clear
end end
def add_job(user, args) def add_job(user, args)
Sidekiq::Client.push( Sidekiq::Client.push(
'class' => 'AuthorizedProjectsWorker', 'class' => 'AuthorizedProjectsWorker',
'queue' => 'authorized_projects', 'queue' => queue,
'args' => args, 'args' => args,
'meta.user' => user.username 'meta.user' => user.username
) )
raise 'Not enqueued!' if Sidekiq::Queue.new(queue).size.zero?
end end
it 'returns info about the deleted jobs' do it 'returns info about the deleted jobs' do
...@@ -55,7 +57,7 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do ...@@ -55,7 +57,7 @@ RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do
end end
context 'when no required params are provided' do context 'when no required params are provided' do
let(:variables) { { queue_name: 'authorized_projects' } } let(:variables) { { queue_name: queue } }
it_behaves_like 'a mutation that returns errors in the response', it_behaves_like 'a mutation that returns errors in the response',
errors: ['No metadata provided'] errors: ['No metadata provided']
......
...@@ -163,9 +163,15 @@ RSpec.describe 'Creating a Snippet' do ...@@ -163,9 +163,15 @@ RSpec.describe 'Creating a Snippet' do
context 'when there are uploaded files' do context 'when there are uploaded files' do
shared_examples 'expected files argument' do |file_value, expected_value| shared_examples 'expected files argument' do |file_value, expected_value|
let(:uploaded_files) { file_value } let(:uploaded_files) { file_value }
let(:ham) { build(:snippet) }
let(:creation_response) do
::ServiceResponse.error(message: 'urk', payload: { snippet: ham })
end
it do it do
expect(::Snippets::CreateService).to receive(:new).with(nil, user, hash_including(files: expected_value)) expect(::Snippets::CreateService).to receive(:new)
.with(nil, user, hash_including(files: expected_value))
.and_return(double(execute: creation_response))
subject subject
end end
......
...@@ -42,7 +42,6 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) ...@@ -42,7 +42,6 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
describe 'scalar fields' do describe 'scalar fields' do
let(:path) { path_prefix } let(:path) { path_prefix }
let(:version_fields) { query_graphql_field(:sha) }
before do before do
post_query post_query
...@@ -50,7 +49,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) ...@@ -50,7 +49,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
{ id: ->(x) { x.to_global_id.to_s }, sha: ->(x) { x.sha } }.each do |field, value| { id: ->(x) { x.to_global_id.to_s }, sha: ->(x) { x.sha } }.each do |field, value|
describe ".#{field}" do describe ".#{field}" do
let(:version_fields) { query_graphql_field(field) } let(:version_fields) { field }
it "retrieves the #{field}" do it "retrieves the #{field}" do
expect(data).to match(a_hash_including(field.to_s => value[version])) expect(data).to match(a_hash_including(field.to_s => value[version]))
......
...@@ -29,8 +29,8 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do ...@@ -29,8 +29,8 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do
let(:design_fields) do let(:design_fields) do
[ [
query_graphql_field(:filename), :filename,
query_graphql_field(:project, nil, query_graphql_field(:id)) query_graphql_field(:project, :id)
] ]
end end
...@@ -173,7 +173,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do ...@@ -173,7 +173,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do
let(:result_fields) { { 'version' => id_hash(version) } } let(:result_fields) { { 'version' => id_hash(version) } }
let(:object_fields) do let(:object_fields) do
design_fields + [query_graphql_field(:version, nil, query_graphql_field(:id))] design_fields + [query_graphql_field(:version, :id)]
end end
let(:no_argument_error) { missing_required_argument(path, :id) } let(:no_argument_error) { missing_required_argument(path, :id) }
......
...@@ -36,7 +36,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do ...@@ -36,7 +36,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:path) { path_prefix } let(:path) { path_prefix }
let(:release_fields) do let(:release_fields) do
query_graphql_field(%{ %{
tagName tagName
tagPath tagPath
description description
...@@ -45,7 +45,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do ...@@ -45,7 +45,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
createdAt createdAt
releasedAt releasedAt
upcomingRelease upcomingRelease
}) }
end end
before do before do
...@@ -233,7 +233,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do ...@@ -233,7 +233,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:path) { path_prefix } let(:path) { path_prefix }
let(:release_fields) do let(:release_fields) do
query_graphql_field('description') 'description'
end end
before do before do
...@@ -394,10 +394,10 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do ...@@ -394,10 +394,10 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do
let(:current_user) { developer } let(:current_user) { developer }
let(:release_fields) do let(:release_fields) do
query_graphql_field(%{ %{
releasedAt releasedAt
upcomingRelease upcomingRelease
}) }
end end
before do before do
......
...@@ -48,37 +48,38 @@ RSpec.describe 'query terraform states' do ...@@ -48,37 +48,38 @@ RSpec.describe 'query terraform states' do
let(:current_user) { project.creator } let(:current_user) { project.creator }
let(:data) { graphql_data.dig('project', 'terraformStates') } let(:data) { graphql_data.dig('project', 'terraformStates') }
let(:download_path) do
expose_path(
api_v4_projects_terraform_state_versions_path(
id: project.id,
name: terraform_state.name,
serial: latest_version.version
)
)
end
before do before do
post_graphql(query, current_user: current_user) post_graphql(query, current_user: current_user)
end end
it 'returns terraform state data', :aggregate_failures do it 'returns terraform state data' do
state = data.dig('nodes', 0) expect(data['nodes']).to contain_exactly({
version = state['latestVersion'] 'id' => global_id_of(terraform_state),
'name' => terraform_state.name,
expect(state['id']).to eq(terraform_state.to_global_id.to_s) 'lockedAt' => terraform_state.locked_at.iso8601,
expect(state['name']).to eq(terraform_state.name) 'createdAt' => terraform_state.created_at.iso8601,
expect(state['lockedAt']).to eq(terraform_state.locked_at.iso8601) 'updatedAt' => terraform_state.updated_at.iso8601,
expect(state['createdAt']).to eq(terraform_state.created_at.iso8601) 'lockedByUser' => { 'id' => global_id_of(terraform_state.locked_by_user) },
expect(state['updatedAt']).to eq(terraform_state.updated_at.iso8601) 'latestVersion' => {
expect(state.dig('lockedByUser', 'id')).to eq(terraform_state.locked_by_user.to_global_id.to_s) 'id' => eq(latest_version.to_global_id.to_s),
'serial' => eq(latest_version.version),
expect(version['id']).to eq(latest_version.to_global_id.to_s) 'downloadPath' => eq(download_path),
expect(version['serial']).to eq(latest_version.version) 'createdAt' => eq(latest_version.created_at.iso8601),
expect(version['downloadPath']).to eq( 'updatedAt' => eq(latest_version.updated_at.iso8601),
expose_path( 'createdByUser' => { 'id' => eq(latest_version.created_by_user.to_global_id.to_s) },
api_v4_projects_terraform_state_versions_path( 'job' => { 'name' => eq(latest_version.build.name) }
id: project.id, }
name: terraform_state.name, })
serial: latest_version.version
)
)
)
expect(version['createdAt']).to eq(latest_version.created_at.iso8601)
expect(version['updatedAt']).to eq(latest_version.updated_at.iso8601)
expect(version.dig('createdByUser', 'id')).to eq(latest_version.created_by_user.to_global_id.to_s)
expect(version.dig('job', 'name')).to eq(latest_version.build.name)
end end
it 'returns count of terraform states' do it 'returns count of terraform states' do
......
...@@ -5,35 +5,33 @@ require 'spec_helper' ...@@ -5,35 +5,33 @@ require 'spec_helper'
RSpec.describe 'getting project information' do RSpec.describe 'getting project information' do
include GraphqlHelpers include GraphqlHelpers
let(:group) { create(:group) } let_it_be(:group) { create(:group) }
let(:project) { create(:project, :repository, group: group) } let_it_be(:project) { create(:project, :repository, group: group) }
let(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let(:fields) { all_graphql_fields_for(Project, max_depth: 2, excluded: %w(jiraImports services)) }
let(:query) do let(:query) do
graphql_query_for( graphql_query_for(:project, { full_path: project.full_path }, fields)
'project',
{ 'fullPath' => project.full_path },
all_graphql_fields_for('project'.to_s.classify, excluded: %w(jiraImports services))
)
end end
context 'when the user has full access to the project' do context 'when the user has full access to the project' do
let(:full_access_query) do let(:full_access_query) do
graphql_query_for('project', 'fullPath' => project.full_path) graphql_query_for(:project, { full_path: project.full_path },
all_graphql_fields_for('Project', max_depth: 2))
end end
before do before do
project.add_maintainer(current_user) project.add_maintainer(current_user)
end end
it 'includes the project' do it 'includes the project', :use_clean_rails_memory_store_caching, :request_store do
post_graphql(query, current_user: current_user) post_graphql(full_access_query, current_user: current_user)
expect(graphql_data['project']).not_to be_nil expect(graphql_data['project']).not_to be_nil
end end
end end
context 'when the user has access to the project' do context 'when the user has access to the project', :use_clean_rails_memory_store_caching, :request_store do
before do before do
project.add_developer(current_user) project.add_developer(current_user)
end end
...@@ -55,10 +53,12 @@ RSpec.describe 'getting project information' do ...@@ -55,10 +53,12 @@ RSpec.describe 'getting project information' do
create(:ci_pipeline, project: project) create(:ci_pipeline, project: project)
end end
let(:fields) { query_nodes(:pipelines) }
it 'is included in the pipelines connection' do it 'is included in the pipelines connection' do
post_graphql(query, current_user: current_user) post_graphql(query, current_user: current_user)
expect(graphql_data['project']['pipelines']['edges'].size).to eq(1) expect(graphql_data_at(:project, :pipelines, :nodes)).to contain_exactly(a_kind_of(Hash))
end end
end end
...@@ -109,7 +109,7 @@ RSpec.describe 'getting project information' do ...@@ -109,7 +109,7 @@ RSpec.describe 'getting project information' do
end end
describe 'performance' do describe 'performance' do
before do before_all do
project.add_developer(current_user) project.add_developer(current_user)
mrs = create_list(:merge_request, 10, :closed, :with_head_pipeline, mrs = create_list(:merge_request, 10, :closed, :with_head_pipeline,
source_project: project, source_project: project,
......
...@@ -26,27 +26,35 @@ RSpec.shared_examples 'a GraphQL type with design fields' do ...@@ -26,27 +26,35 @@ RSpec.shared_examples 'a GraphQL type with design fields' do
end end
describe '#image' do describe '#image' do
let_it_be(:current_user) { create(:user) }
let(:schema) { GitlabSchema } let(:schema) { GitlabSchema }
let(:query) { GraphQL::Query.new(schema) } let(:query) { GraphQL::Query.new(schema) }
let(:context) { double('Context', schema: schema, query: query, parent: nil) } let(:context) { query.context }
let(:field) { described_class.fields['image'] } let(:field) { described_class.fields['image'] }
let(:args) { GraphQL::Query::Arguments::NO_ARGS } let(:args) { GraphQL::Query::Arguments::NO_ARGS }
let(:instance) do let(:instance) { instantiate(object_id) }
let(:instance_b) { instantiate(object_id_b) }
def instantiate(object_id)
object = GitlabSchema.sync_lazy(GitlabSchema.object_from_id(object_id)) object = GitlabSchema.sync_lazy(GitlabSchema.object_from_id(object_id))
object_type.authorized_new(object, query.context) object_type.authorized_new(object, query.context)
end end
let(:instance_b) do def resolve_image(instance)
object_b = GitlabSchema.sync_lazy(GitlabSchema.object_from_id(object_id_b)) field.resolve_field(instance, args, context)
object_type.authorized_new(object_b, query.context) end
before do
context[:current_user] = current_user
allow(Ability).to receive(:allowed?).with(current_user, :read_design, anything).and_return(true)
allow(context).to receive(:parent).and_return(nil)
end end
it 'resolves to the design image URL' do it 'resolves to the design image URL' do
image = field.resolve_field(instance, args, context)
sha = design.versions.first.sha sha = design.versions.first.sha
url = ::Gitlab::Routing.url_helpers.project_design_management_designs_raw_image_url(design.project, design, sha) url = ::Gitlab::Routing.url_helpers.project_design_management_designs_raw_image_url(design.project, design, sha)
expect(image).to eq(url) expect(resolve_image(instance)).to eq(url)
end end
it 'has better than O(N) peformance', :request_store do it 'has better than O(N) peformance', :request_store do
...@@ -68,10 +76,10 @@ RSpec.shared_examples 'a GraphQL type with design fields' do ...@@ -68,10 +76,10 @@ RSpec.shared_examples 'a GraphQL type with design fields' do
# = 10 # = 10
expect(instance).not_to eq(instance_b) # preload designs themselves. expect(instance).not_to eq(instance_b) # preload designs themselves.
expect do expect do
image_a = field.resolve_field(instance, args, context) image_a = resolve_image(instance)
image_b = field.resolve_field(instance, args, context) image_b = resolve_image(instance)
image_c = field.resolve_field(instance_b, args, context) image_c = resolve_image(instance_b)
image_d = field.resolve_field(instance_b, args, context) image_d = resolve_image(instance_b)
expect(image_a).to eq(image_b) expect(image_a).to eq(image_b)
expect(image_c).not_to eq(image_b) expect(image_c).not_to eq(image_b)
expect(image_c).to eq(image_d) expect(image_c).to eq(image_d)
......
...@@ -2,14 +2,12 @@ ...@@ -2,14 +2,12 @@
RSpec.shared_examples 'no Jira import data present' do RSpec.shared_examples 'no Jira import data present' do
it 'returns none' do it 'returns none' do
expect(resolve_imports).to eq JiraImportState.none expect(resolve_imports).to be_empty
end end
end end
RSpec.shared_examples 'no Jira import access' do RSpec.shared_examples 'no Jira import access' do
it 'raises error' do it 'returns nil' do
expect do expect(resolve_imports).to be_nil
resolve_imports
end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end end
end end
...@@ -13,6 +13,8 @@ RSpec.shared_examples 'a mutation that returns top-level errors' do |errors: []| ...@@ -13,6 +13,8 @@ RSpec.shared_examples 'a mutation that returns top-level errors' do |errors: []|
it do it do
post_graphql_mutation(mutation, current_user: current_user) post_graphql_mutation(mutation, current_user: current_user)
expect(graphql_errors).to be_present
error_messages = graphql_errors.map { |e| e['message'] } error_messages = graphql_errors.map { |e| e['message'] }
expect(error_messages).to match_errors expect(error_messages).to match_errors
......
...@@ -13,7 +13,8 @@ end ...@@ -13,7 +13,8 @@ end
RSpec.shared_examples 'can raise spam flag' do RSpec.shared_examples 'can raise spam flag' do
it 'spam parameters are passed to the service' do it 'spam parameters are passed to the service' do
expect(service).to receive(:new).with(anything, anything, hash_including(api: true, request: instance_of(ActionDispatch::Request))) args = [anything, anything, hash_including(api: true, request: instance_of(ActionDispatch::Request))]
expect(service).to receive(:new).with(*args).and_call_original
subject subject
end end
...@@ -39,7 +40,9 @@ RSpec.shared_examples 'can raise spam flag' do ...@@ -39,7 +40,9 @@ RSpec.shared_examples 'can raise spam flag' do
end end
it 'request parameter is not passed to the service' do it 'request parameter is not passed to the service' do
expect(service).to receive(:new).with(anything, anything, hash_not_including(request: instance_of(ActionDispatch::Request))) expect(service).to receive(:new)
.with(anything, anything, hash_not_including(request: instance_of(ActionDispatch::Request)))
.and_call_original
subject subject
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
RSpec.shared_examples 'no project services' do RSpec.shared_examples 'no project services' do
it 'returns empty collection' do it 'returns empty collection' do
expect(resolve_services).to eq [] expect(resolve_services).to be_empty
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment