diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /spec/graphql/resolvers | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'spec/graphql/resolvers')
19 files changed, 289 insertions, 171 deletions
diff --git a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb index c042f6dac19..14ebe85d80e 100644 --- a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb +++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb @@ -39,8 +39,8 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do end context 'filtering by domain' do - let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) } - let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'Cilium', domain: :threat_monitoring) } + let_it_be(:alert1) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) } + let_it_be(:alert2) { create(:alert_management_alert, project: project, monitoring_tool: 'other', domain: :threat_monitoring) } let_it_be(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') } let(:args) { { domain: 'operations' } } diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb index 3ff6d8f4347..7a6104fc503 100644 --- a/spec/graphql/resolvers/ci/config_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb @@ -37,13 +37,15 @@ RSpec.describe Resolvers::Ci::ConfigResolver do merged_yaml: content, jobs: [], errors: [], - warnings: [] + warnings: [], + includes: [] ) end it 'lints the ci config file and returns the merged yaml file' do expect(response[:status]).to eq(:valid) expect(response[:merged_yaml]).to eq(content) + expect(response[:includes]).to eq([]) expect(response[:errors]).to be_empty expect(::Gitlab::Ci::Lint).to have_received(:new).with(current_user: user, project: project, sha: sha) end @@ -69,7 +71,8 @@ RSpec.describe Resolvers::Ci::ConfigResolver do jobs: [], merged_yaml: content, errors: ['Invalid configuration format'], - warnings: [] + warnings: [], + includes: [] ) end diff --git a/spec/graphql/resolvers/concerns/resolves_ids_spec.rb b/spec/graphql/resolvers/concerns/resolves_ids_spec.rb index 1dd27c0eff0..84741b7a603 100644 --- a/spec/graphql/resolvers/concerns/resolves_ids_spec.rb +++ b/spec/graphql/resolvers/concerns/resolves_ids_spec.rb @@ -3,33 +3,32 @@ require 'spec_helper' RSpec.describe ResolvesIds do + include GraphqlHelpers + # 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] } + let(:ids) { global_id_of(model_name: 'Project', id: 6) } it 'returns the correct array' do - expect(resolve_ids).to match_array(['6']) + expect(resolve_ids).to contain_exactly('6') end end context 'with a single issue' do - let(:ids) { 'gid://gitlab/Issue/9' } - let(:type) { ::Types::GlobalIDType[::Issue] } + let(:ids) { global_id_of(model_name: 'Issue', id: 9) } it 'returns the correct array' do - expect(resolve_ids).to match_array(['9']) + expect(resolve_ids).to contain_exactly('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] } + let(:ids) { [7, 13, 21].map { global_id_of(model_name: 'User', id: _1) } } it 'returns the correct array' do - expect(resolve_ids).to match_array(%w[7 13 21]) + expect(resolve_ids).to eq %w[7 13 21] end end @@ -38,6 +37,6 @@ RSpec.describe ResolvesIds do end def resolve_ids - mock_resolver.resolve_ids(ids, type) + mock_resolver.resolve_ids(ids) end end diff --git a/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb index a16e8821cb5..3fe1ec4b5a4 100644 --- a/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Resolvers::DesignManagement::DesignAtVersionResolver do let(:current_user) { user } let(:object) { issue.design_collection } - let(:global_id) { GitlabSchema.id_from_object(design_at_version).to_s } + let(:global_id) { GitlabSchema.id_from_object(design_at_version) } let(:design_at_version) { ::DesignManagement::DesignAtVersion.new(design: design_a, version: version_a) } diff --git a/spec/graphql/resolvers/design_management/design_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_resolver_spec.rb index 4c8b3116875..0915dddf438 100644 --- a/spec/graphql/resolvers/design_management/design_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/design_resolver_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Resolvers::DesignManagement::DesignResolver do create(:design, issue: create(:issue, project: project), versions: [create(:design_version)]) end - let(:args) { { id: GitlabSchema.id_from_object(first_design).to_s } } + let(:args) { { id: GitlabSchema.id_from_object(first_design) } } let(:gql_context) { { current_user: current_user } } before do @@ -50,7 +50,7 @@ RSpec.describe Resolvers::DesignManagement::DesignResolver do end context 'when both arguments have been passed' do - let(:args) { { filename: first_design.filename, id: GitlabSchema.id_from_object(first_design).to_s } } + let(:args) { { filename: first_design.filename, id: GitlabSchema.id_from_object(first_design) } } it 'generates an error' do expect_graphql_error_to_be_created(::Gitlab::Graphql::Errors::ArgumentError, /may/) do @@ -71,15 +71,6 @@ RSpec.describe Resolvers::DesignManagement::DesignResolver do expect(resolve_design).to be_nil end end - - context 'the ID does not belong to a design at all' do - let(:args) { { id: global_id_of(issue) } } - let(:msg) { /does not represent an instance of DesignManagement::Design/ } - - it 'complains meaningfully' do - expect { resolve_design }.to raise_error(msg) - end - end end context 'by filename' do diff --git a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb index b091e58b06f..64eae14d888 100644 --- a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb @@ -109,6 +109,8 @@ RSpec.describe Resolvers::DesignManagement::DesignsResolver do end def resolve_designs - resolve(described_class, obj: issue.design_collection, args: args, ctx: gql_context) + Gitlab::Graphql::Lazy.force( + resolve(described_class, obj: issue.design_collection, args: args, ctx: gql_context) + ) end end diff --git a/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb index 8b9874c3580..00f37a8e5f6 100644 --- a/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb @@ -50,15 +50,6 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do it { is_expected.to be_nil } end - - context 'we pass the id of something that is not a design_version' do - let(:params) { { id: global_id_of(project) } } - let(:appropriate_error) { ::GraphQL::CoercionError } - - it 'raises an appropriate error' do - expect { result }.to raise_error(appropriate_error) - end - end end def resolve_version(obj, context = { current_user: current_user }) diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb index d98138f6385..8eab0222cf6 100644 --- a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb @@ -18,8 +18,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do let(:project) { issue.project } let(:params) { {} } let(:current_user) { authorized_user } - let(:parent_args) { { irrelevant: 1.2 } } - let(:parent) { double('Parent', parent: nil, irep_node: double(arguments: parent_args)) } + let(:query_context) { { current_user: current_user } } before do enable_design_management @@ -107,7 +106,9 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do end context 'by at_version in parent' do - let(:parent_args) { { atVersion: global_id_of(first_version) } } + before do + query_context[:at_version_argument] = first_version.to_global_id + end it_behaves_like 'a query for all_versions up to the first_version' end @@ -126,8 +127,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do it_behaves_like 'a source of versions' end - def resolve_versions(obj, context = { current_user: current_user }) - eager_resolve(resolver, obj: obj, parent: parent, args: params, ctx: context) + def resolve_versions(obj) + eager_resolve(resolver, obj: obj, args: params, ctx: query_context) end end end 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 2aef483ac95..f80b33e644e 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 @@ -8,28 +8,23 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do let_it_be(:project) { create(:project) } let_it_be(:current_user) { create(:user) } - let(:issue_details_service) { spy('ErrorTracking::IssueDetailsService') } + let(:issue_details_service) { instance_double('ErrorTracking::IssueDetailsService') } + let(:service_response) { {} } - specify do - expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryDetailedErrorType) + before_all do + project.add_developer(current_user) end before do - project.add_developer(current_user) - allow(ErrorTracking::IssueDetailsService) .to receive(:new) - .and_return issue_details_service - end + .and_return(issue_details_service) - shared_examples 'it resolves to nil' do - it 'resolves to nil' do - allow(issue_details_service).to receive(:execute) - .and_return(issue: nil) + allow(issue_details_service).to receive(:execute).and_return(service_response) + end - result = resolve_error(args) - expect(result).to be_nil - end + specify do + expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryDetailedErrorType) end describe '#resolve' do @@ -41,13 +36,9 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do expect(issue_details_service).to have_received(:execute) end - context 'error matched' do - let(:detailed_error) { build(:error_tracking_sentry_detailed_error) } - - before do - allow(issue_details_service).to receive(:execute) - .and_return(issue: detailed_error) - end + context 'when error matches' do + let(:detailed_error) { build_stubbed(:error_tracking_sentry_detailed_error) } + let(:service_response) { { issue: detailed_error } } it 'resolves to a detailed error' do expect(resolve_error(args)).to eq detailed_error @@ -58,24 +49,23 @@ RSpec.describe Resolvers::ErrorTracking::SentryDetailedErrorResolver do end end - context 'if id does not match issue' do - it_behaves_like 'it resolves to nil' - end - - context 'blank id' do - let(:args) { { id: '' } } + context 'when id does not match issue' do + let(:service_response) { { issue: nil } } - it 'responds with an error' do - expect { resolve_error(args) }.to raise_error(::GraphQL::CoercionError) + it 'resolves to nil' do + result = resolve_error(args) + expect(result).to be_nil end end end + private + def resolve_error(args = {}, context = { current_user: current_user }) resolve(described_class, obj: project, args: args, ctx: context) end def issue_global_id(issue_id) - Gitlab::ErrorTracking::DetailedError.new(id: issue_id).to_global_id.to_s + Gitlab::ErrorTracking::DetailedError.new(id: issue_id).to_global_id end end diff --git a/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb index 20c2bdcd4e1..5834faea97e 100644 --- a/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb +++ b/spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb @@ -8,18 +8,22 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorCollectionResolver do let_it_be(:project) { create(:project) } let_it_be(:current_user) { create(:user) } - let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') } + let(:list_issues_service) { instance_double('ErrorTracking::ListIssuesService') } - specify do - expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryErrorCollectionType) + before_all do + project.add_developer(current_user) end before do - project.add_developer(current_user) - allow(ErrorTracking::ListIssuesService) .to receive(:new) .and_return list_issues_service + + allow(list_issues_service).to receive(:external_url) + end + + specify do + expect(described_class).to have_nullable_graphql_type(Types::ErrorTracking::SentryErrorCollectionType) end describe '#resolve' do @@ -34,8 +38,7 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorCollectionResolver do .to receive(:external_url) .and_return(fake_url) - result = resolve_error_collection - expect(result.external_url).to eq fake_url + expect(resolve_error_collection.external_url).to eq fake_url end it 'provides the project' do diff --git a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb index 68badb8e333..65b6c551dde 100644 --- a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb +++ b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do let_it_be(:current_user) { create(:user) } let_it_be(:error_collection) { Gitlab::ErrorTracking::ErrorCollection.new(project: project) } - let(:list_issues_service) { spy('ErrorTracking::ListIssuesService') } + let(:list_issues_service) { instance_double('ErrorTracking::ListIssuesService') } let(:issues) { nil } let(:pagination) { nil } @@ -19,23 +19,25 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do end describe '#resolve' do + before do + allow(ErrorTracking::ListIssuesService) + .to receive(:new) + .and_return list_issues_service + + allow(list_issues_service).to receive(:execute).and_return({}) + end + context 'with insufficient user permission' do - let(:user) { create(:user) } + let(:current_user) { create(:user) } it 'returns nil' do - context = { current_user: user } - - expect(resolve_errors({}, context)).to eq nil + expect(resolve_errors).to eq nil end end context 'with sufficient permission' do - before do + before_all do project.add_developer(current_user) - - allow(ErrorTracking::ListIssuesService) - .to receive(:new) - .and_return list_issues_service end context 'when after arg given' do @@ -52,14 +54,9 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do end context 'when no issues fetched' do - before do - allow(list_issues_service) - .to receive(:execute) - .and_return( - issues: nil - ) - end it 'returns nil' do + expect(list_issues_service).to receive(:execute).and_return(issues: nil) + expect(resolve_errors).to eq nil end end diff --git a/spec/graphql/resolvers/incident_management/timeline_events_resolver_spec.rb b/spec/graphql/resolvers/incident_management/timeline_events_resolver_spec.rb new file mode 100644 index 00000000000..046cf242d56 --- /dev/null +++ b/spec/graphql/resolvers/incident_management/timeline_events_resolver_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::IncidentManagement::TimelineEventsResolver do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:incident) { create(:incident, project: project) } + let_it_be(:first_timeline_event) do + create(:incident_management_timeline_event, project: project, incident: incident) + end + + let_it_be(:second_timeline_event) do + create(:incident_management_timeline_event, project: project, incident: incident) + end + + let(:args) { { incident_id: incident.to_global_id } } + let(:resolver) { described_class } + + subject(:resolved_timeline_events) { sync(resolve_timeline_events(args, current_user: current_user).to_a) } + + before do + project.add_guest(current_user) + end + + specify do + expect(resolver).to have_nullable_graphql_type(Types::IncidentManagement::TimelineEventType.connection_type) + end + + it 'returns timeline events', :aggregate_failures do + expect(resolved_timeline_events.length).to eq(2) + expect(resolved_timeline_events.first).to be_a(::IncidentManagement::TimelineEvent) + end + + context 'when user does not have permissions' do + let(:non_member) { create(:user) } + + subject(:resolved_timeline_events) { sync(resolve_timeline_events(args, current_user: non_member).to_a) } + + before do + project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) + end + + it 'returns no timeline events' do + expect(resolved_timeline_events.length).to eq(0) + end + end + + context 'when resolving a single item' do + let(:resolver) { described_class.single } + + subject(:resolved_timeline_event) { sync(resolve_timeline_events(args, current_user: current_user)) } + + context 'when id given' do + let(:args) { { incident_id: incident.to_global_id, id: first_timeline_event.to_global_id } } + + it 'returns the timeline event' do + expect(resolved_timeline_event).to eq(first_timeline_event) + end + end + end + + private + + def resolve_timeline_events(args = {}, context = { current_user: current_user }) + resolve(resolver, obj: incident, args: args, ctx: context) + end +end diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index 81aeee0a3d2..e6ec9d8c895 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -389,6 +389,34 @@ RSpec.describe Resolvers::IssuesResolver do end end + describe 'filtering by crm' do + let_it_be(:organization) { create(:organization, group: group) } + let_it_be(:contact1) { create(:contact, group: group, organization: organization) } + let_it_be(:contact2) { create(:contact, group: group, organization: organization) } + let_it_be(:contact3) { create(:contact, group: group) } + let_it_be(:crm_issue1) { create(:issue, project: project) } + let_it_be(:crm_issue2) { create(:issue, project: project) } + let_it_be(:crm_issue3) { create(:issue, project: project) } + + before_all do + create(:issue_customer_relations_contact, issue: crm_issue1, contact: contact1) + create(:issue_customer_relations_contact, issue: crm_issue2, contact: contact2) + create(:issue_customer_relations_contact, issue: crm_issue3, contact: contact3) + end + + context 'contact' do + it 'returns only the issues for the contact' do + expect(resolve_issues({ crm_contact_id: contact1.id })).to contain_exactly(crm_issue1) + end + end + + context 'organization' do + it 'returns only the issues for the contact' do + expect(resolve_issues({ crm_organization_id: organization.id })).to contain_exactly(crm_issue1, crm_issue2) + end + end + end + describe 'sorting' do context 'when sorting by created' do it 'sorts issues ascending' do @@ -603,13 +631,13 @@ RSpec.describe Resolvers::IssuesResolver do end it 'finds a specific issue with iid', :request_store do - result = batch_sync(max_queries: 6) { resolve_issues(iid: issue1.iid).to_a } + result = batch_sync(max_queries: 7) { 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: 6) do + result = batch_sync(max_queries: 7) do [issue1, issue2] .map { |issue| resolve_issues(iid: issue.iid.to_s) } .flat_map(&:to_a) @@ -619,7 +647,7 @@ RSpec.describe Resolvers::IssuesResolver do end it 'finds a specific issue with iids', :request_store do - result = batch_sync(max_queries: 6) do + result = batch_sync(max_queries: 7) do resolve_issues(iids: [issue1.iid]).to_a end diff --git a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb index c757c876616..a52dee59bc6 100644 --- a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb @@ -9,10 +9,23 @@ RSpec.describe Resolvers::PackagePipelinesResolver do let_it_be(:pipelines) { create_list(:ci_pipeline, 3, project: package.project) } let(:user) { package.project.first_owner } - let(:args) { {} } describe '#resolve' do - subject { resolve(described_class, obj: package, args: args, ctx: { current_user: user }) } + let(:returned_pipelines) { graphql_dig_at(subject, 'data', 'package', 'pipelines', 'nodes') } + let(:returned_errors) { graphql_dig_at(subject, 'errors', 'message') } + let(:pagination_args) { {} } + let(:query) do + pipelines_nodes = 'nodes { id }' + graphql_query_for( + :package, + { id: global_id_of(package) }, + query_graphql_field('pipelines', pagination_args, pipelines_nodes) + ) + end + + subject do + GitlabSchema.execute(query, context: { current_user: user }) + end before do pipelines.each do |pipeline| @@ -20,67 +33,115 @@ RSpec.describe Resolvers::PackagePipelinesResolver do end end - it { is_expected.to contain_exactly(*pipelines) } + it 'contains the expected pipelines' do + expect_to_contain_exactly(*pipelines) + end + + context 'with valid after' do + let(:pagination_args) { { first: 1, after: encode_cursor(id: pipelines[1].id) } } + + it 'contains the expected pipelines' do + expect_to_contain_exactly(pipelines[0]) + end + end + + context 'with valid before' do + let(:pagination_args) { { last: 1, before: encode_cursor(id: pipelines[1].id) } } + + it 'contains the expected pipelines' do + expect_to_contain_exactly(pipelines[2]) + end + end context 'with invalid after' do - let(:args) { { first: 1, after: 'not_json_string' } } + let(:pagination_args) { { first: 1, after: 'not_json_string' } } it 'generates an argument error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do - subject - end + expect(returned_errors).to include('Please provide a valid cursor') end end context 'with invalid after key' do - let(:args) { { first: 1, after: encode_cursor(foo: 3) } } + let(:pagination_args) { { first: 1, after: encode_cursor(foo: 3) } } it 'generates an argument error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do - subject - end + expect(returned_errors).to include('Please provide a valid cursor') end end context 'with invalid before' do - let(:args) { { last: 1, before: 'not_json_string' } } + let(:pagination_args) { { last: 1, before: 'not_json_string' } } it 'generates an argument error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do - subject - end + expect(returned_errors).to include('Please provide a valid cursor') end end context 'with invalid before key' do - let(:args) { { last: 1, before: encode_cursor(foo: 3) } } + let(:pagination_args) { { last: 1, before: encode_cursor(foo: 3) } } it 'generates an argument error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do - subject - end + expect(returned_errors).to include('Please provide a valid cursor') end end - context 'field options' do - let(:field) do - field_options = described_class.field_options.merge( - owner: resolver_parent, - name: 'dummy_field' - ) - ::Types::BaseField.new(**field_options) - end + context 'with unauthorized user' do + let_it_be(:user) { create(:user) } - it 'sets them properly' do - expect(field).not_to be_connection - expect(field.extras).to match_array([:lookahead]) + it 'returns nothing' do + expect(returned_pipelines).to be_nil end end - context 'with unauthorized user' do - let_it_be(:user) { create(:user) } + context 'with many packages' do + let_it_be_with_reload(:other_package) { create(:package, project: package.project) } + let_it_be(:other_pipelines) { create_list(:ci_pipeline, 3, project: package.project) } + + let(:returned_pipelines) do + graphql_dig_at(subject, 'data', 'project', 'packages', 'nodes', 'pipelines', 'nodes') + end - it { is_expected.to be_nil } + let(:query) do + pipelines_query = query_graphql_field('pipelines', pagination_args, 'nodes { id }') + <<~QUERY + { + project(fullPath: "#{package.project.full_path}") { + packages { + nodes { #{pipelines_query} } + } + } + } + QUERY + end + + before do + other_pipelines.each do |pipeline| + create(:package_build_info, package: other_package, pipeline: pipeline) + end + end + + it 'contains the expected pipelines' do + expect_to_contain_exactly(*(pipelines + other_pipelines)) + end + + it 'handles n+1 situations' do + control = ActiveRecord::QueryRecorder.new do + GitlabSchema.execute(query, context: { current_user: user }) + end + + create_package_with_pipelines(package.project) + + expectation = expect { GitlabSchema.execute(query, context: { current_user: user }) } + + expectation.not_to exceed_query_limit(control) + end + + def create_package_with_pipelines(project) + extra_package = create(:package, project: project) + create_list(:ci_pipeline, 3, project: project).each do |pipeline| + create(:package_build_info, package: extra_package, pipeline: pipeline) + end + end end def encode_cursor(json) @@ -89,5 +150,25 @@ RSpec.describe Resolvers::PackagePipelinesResolver do nonce: true ) end + + def expect_to_contain_exactly(*pipelines) + entities = pipelines.map { |pipeline| a_graphql_entity_for(pipeline) } + expect(returned_pipelines).to match_array(entities) + end + end + + describe '.field options' do + let(:field) do + field_options = described_class.field_options.merge( + owner: resolver_parent, + name: 'dummy_field' + ) + ::Types::BaseField.new(**field_options) + end + + it 'sets them properly' do + expect(field).not_to be_connection + expect(field.extras).to match_array([:lookahead]) + end end end diff --git a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb index b963f2509db..1d04db3ea6e 100644 --- a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb @@ -58,12 +58,6 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do expect(snippets).to contain_exactly(project_snippet, other_project_snippet) 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 context 'when no project is provided' do diff --git a/spec/graphql/resolvers/snippets_resolver_spec.rb b/spec/graphql/resolvers/snippets_resolver_spec.rb index f9feb8901cd..ee9a6e67243 100644 --- a/spec/graphql/resolvers/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/snippets_resolver_spec.rb @@ -40,12 +40,6 @@ RSpec.describe Resolvers::SnippetsResolver do expect(snippets).to contain_exactly(personal_snippet, project_snippet) end - - it 'returns an error if the param id is invalid' do - expect do - resolve_snippets(args: { author_id: 'foo' }) - end.to raise_error(GraphQL::CoercionError) - end end it 'returns the snippets by type' do @@ -61,12 +55,6 @@ RSpec.describe Resolvers::SnippetsResolver do expect(snippets).to contain_exactly(project_snippet, other_project_snippet) end - - it 'returns an error if the param id is invalid' do - expect do - resolve_snippets(args: { project_id: 'foo' }) - end.to raise_error(GraphQL::CoercionError) - end end it 'returns the snippets by visibility' do @@ -98,16 +86,6 @@ RSpec.describe Resolvers::SnippetsResolver do expect(found).to match_array(snippets) end - it 'returns an error if the id cannot be coerced' do - args = { - ids: [personal_snippet.to_global_id, 'foo'] - } - - expect do - resolve_snippets(args: args) - end.to raise_error(GraphQL::CoercionError, '"foo" is not a valid Global ID') - end - it 'generates an error if both project and author are provided' do expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do args = { diff --git a/spec/graphql/resolvers/timelog_resolver_spec.rb b/spec/graphql/resolvers/timelog_resolver_spec.rb index 84fa2932829..da2747fdf72 100644 --- a/spec/graphql/resolvers/timelog_resolver_spec.rb +++ b/spec/graphql/resolvers/timelog_resolver_spec.rb @@ -265,7 +265,7 @@ RSpec.describe Resolvers::TimelogResolver do 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(:args) { { project_id: global_id_of(project) } } let(:extra_args) { {} } it 'pagination returns `default_max_page_size` and sets `has_next_page` true' do diff --git a/spec/graphql/resolvers/users/snippets_resolver_spec.rb b/spec/graphql/resolvers/users/snippets_resolver_spec.rb index 04fe3213a99..12baed2560e 100644 --- a/spec/graphql/resolvers/users/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/users/snippets_resolver_spec.rb @@ -64,16 +64,6 @@ RSpec.describe Resolvers::Users::SnippetsResolver do expect(found).to match_array(snippets) end - - it 'returns an error if the gid is invalid' do - args = { - ids: [global_id_of(private_personal_snippet), 'foo'] - } - - expect do - resolve_snippets(args: args) - end.to raise_error(GraphQL::CoercionError) - end end context 'when user profile is private' do diff --git a/spec/graphql/resolvers/work_item_resolver_spec.rb b/spec/graphql/resolvers/work_item_resolver_spec.rb index bfa0cf1d8a2..c44ed395102 100644 --- a/spec/graphql/resolvers/work_item_resolver_spec.rb +++ b/spec/graphql/resolvers/work_item_resolver_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Resolvers::WorkItemResolver do let(:current_user) { developer } - subject(:resolved_work_item) { resolve_work_item('id' => work_item.to_gid.to_s) } + subject(:resolved_work_item) { resolve_work_item('id' => work_item.to_gid) } context 'when the user can read the work item' do it { is_expected.to eq(work_item) } |