diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 14:59:07 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-17 14:59:07 +0300 |
commit | 8b573c94895dc0ac0e1d9d59cf3e8745e8b539ca (patch) | |
tree | 544930fb309b30317ae9797a9683768705d664c4 /spec/graphql/resolvers | |
parent | 4b1de649d0168371549608993deac953eb692019 (diff) |
Add latest changes from gitlab-org/gitlab@13-7-stable-eev13.7.0-rc42
Diffstat (limited to 'spec/graphql/resolvers')
29 files changed, 482 insertions, 235 deletions
diff --git a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb index 42830f0024d..c042f6dac19 100644 --- a/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb +++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb @@ -33,11 +33,21 @@ RSpec.describe Resolvers::AlertManagement::AlertResolver do end 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) } 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(:alert3) { create(:alert_management_alert, project: project, monitoring_tool: 'generic') } + + let(:args) { { domain: 'operations' } } + + it { is_expected.to contain_exactly(resolved_alert, ignored_alert, alert3) } + end + describe 'sorting' do # Other sorting examples in spec/finders/alert_management/alerts_finder_spec.rb context 'when sorting by events count' do diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb index e5b9fb57e42..8a24b69eb6f 100644 --- a/spec/graphql/resolvers/base_resolver_spec.rb +++ b/spec/graphql/resolvers/base_resolver_spec.rb @@ -273,4 +273,12 @@ RSpec.describe Resolvers::BaseResolver do end end end + + describe '#offset_pagination' do + let(:instance) { resolver_instance(resolver) } + + it 'is sugar for OffsetActiveRecordRelationConnection.new' do + expect(instance.offset_pagination(User.none)).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) + end + end end diff --git a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb index 4ccf194522f..e7c56a526f4 100644 --- a/spec/graphql/resolvers/board_list_issues_resolver_spec.rb +++ b/spec/graphql/resolvers/board_list_issues_resolver_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Resolvers::BoardListIssuesResolver do end 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]) end diff --git a/spec/graphql/resolvers/board_lists_resolver_spec.rb b/spec/graphql/resolvers/board_lists_resolver_spec.rb index c1d8041a1e0..71ebec4dc7e 100644 --- a/spec/graphql/resolvers/board_lists_resolver_spec.rb +++ b/spec/graphql/resolvers/board_lists_resolver_spec.rb @@ -29,9 +29,7 @@ RSpec.describe Resolvers::BoardListsResolver do context 'with unauthorized user' do it 'raises an error' do - expect do - resolve_board_lists(current_user: unauth_user) - end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + expect(resolve_board_lists(current_user: unauth_user)).to be_nil end end @@ -101,12 +99,6 @@ RSpec.describe Resolvers::BoardListsResolver do end def resolve_board_lists(args: {}, current_user: user) - context = GraphQL::Query::Context.new( - query: OpenStruct.new(schema: nil), - values: { current_user: current_user }, - object: nil - ) - - resolve(described_class, obj: board, args: args, ctx: context ) + resolve(described_class, obj: board, args: args, ctx: { current_user: current_user }) end end diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb new file mode 100644 index 00000000000..6911acdb4ec --- /dev/null +++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::Ci::ConfigResolver do + include GraphqlHelpers + + describe '#resolve' do + before do + yaml_processor_double = instance_double(::Gitlab::Ci::YamlProcessor) + allow(yaml_processor_double).to receive(:execute).and_return(fake_result) + + allow(::Gitlab::Ci::YamlProcessor).to receive(:new).and_return(yaml_processor_double) + end + + context 'with a valid .gitlab-ci.yml' do + let(:fake_result) do + ::Gitlab::Ci::YamlProcessor::Result.new( + ci_config: ::Gitlab::Ci::Config.new(content), + errors: [], + warnings: [] + ) + end + + let_it_be(:content) do + File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_includes.yml')) + end + + it 'lints the ci config file' do + response = resolve(described_class, args: { content: content }, ctx: {}) + + expect(response[:status]).to eq(:valid) + expect(response[:errors]).to be_empty + end + end + + context 'with an invalid .gitlab-ci.yml' do + let(:content) { 'invalid' } + + let(:fake_result) do + Gitlab::Ci::YamlProcessor::Result.new( + ci_config: nil, + errors: ['Invalid configuration format'], + warnings: [] + ) + end + + it 'responds with errors about invalid syntax' do + response = resolve(described_class, args: { content: content }, ctx: {}) + + expect(response[:status]).to eq(:invalid) + expect(response[:errors]).to eq(['Invalid configuration format']) + end + end + end +end diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb index a836c89bd61..46ee74a5f7e 100644 --- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb @@ -17,10 +17,14 @@ RSpec.describe Resolvers::Ci::JobsResolver do describe '#resolve' do context 'when security_report_types is empty' do it "returns all of the pipeline's jobs" do - jobs = resolve(described_class, obj: pipeline, args: {}, ctx: {}) - - job_names = jobs.map(&:name) - expect(job_names).to contain_exactly('Normal job', 'DAST job', 'SAST job', 'Container scanning job') + jobs = resolve(described_class, obj: pipeline) + + expect(jobs).to contain_exactly( + have_attributes(name: 'Normal job'), + have_attributes(name: 'DAST job'), + have_attributes(name: 'SAST job'), + have_attributes(name: 'Container scanning job') + ) end end @@ -30,10 +34,12 @@ RSpec.describe Resolvers::Ci::JobsResolver do ::Types::Security::ReportTypeEnum.values['SAST'].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(job_names).to contain_exactly('DAST job', 'SAST job') + expect(jobs).to contain_exactly( + have_attributes(name: 'DAST job'), + have_attributes(name: 'SAST job') + ) end end end diff --git a/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb b/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb index a408981c08e..241a9e58147 100644 --- a/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/commit_pipelines_resolver_spec.rb @@ -50,6 +50,6 @@ RSpec.describe Resolvers::CommitPipelinesResolver do it 'resolves pipelines for commit and ref' do pipelines = resolve_pipelines - expect(pipelines).to eq([pipeline2, pipeline]) + expect(pipelines.to_a).to eq([pipeline2, pipeline]) end end diff --git a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb index b6fe94a2312..5370f7a7433 100644 --- a/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb +++ b/spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb @@ -4,18 +4,25 @@ require 'spec_helper' RSpec.describe ::CachingArrayResolver do include GraphqlHelpers + include Gitlab::Graphql::Laziness - let_it_be(:non_admins) { create_list(:user, 4, admin: false) } - let(:query_context) { {} } + let_it_be(:admins) { create_list(:user, 4, admin: true) } + let(:query_context) { { current_user: admins.first } } let(:max_page_size) { 10 } 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 mod = described_class Class.new(::Resolvers::BaseResolver) do include mod + type [::Types::UserType], null: true + argument :is_admin, ::GraphQL::BOOLEAN_TYPE, required: false def query_input(is_admin:) is_admin @@ -44,6 +51,8 @@ RSpec.describe ::CachingArrayResolver do Class.new(::Resolvers::BaseResolver) do include mod + type [::Types::UserType], null: true + argument :username, ::GraphQL::STRING_TYPE, required: false def query_input(username:) username @@ -72,7 +81,7 @@ RSpec.describe ::CachingArrayResolver do expect(User).to receive(:from_union).twice.and_call_original 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 expect(results.flat_map(&method(:force))).to match_array(users) @@ -80,19 +89,19 @@ RSpec.describe ::CachingArrayResolver do end 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 expect do - [resolve_users(true), resolve_users(false)].each(&method(:force)) - end.to issue_same_number_of_queries_as { force(resolve_users(nil)) } + [resolve_users(admin: true), resolve_users(admin: false)].each(&method(:force)) + end.to issue_same_number_of_queries_as { force(resolve_users(admin: nil)) } end it 'finds the correct values' do - found_admins = resolve_users(true) - found_others = resolve_users(false) - admins_again = resolve_users(true) - found_all = resolve_users(nil) + found_admins = resolve_users(admin: true) + found_others = resolve_users(admin: false) + admins_again = resolve_users(admin: true) + found_all = resolve_users(admin: nil) expect(force(found_admins)).to match_array(admins) expect(force(found_others)).to match_array(non_admins) @@ -104,37 +113,37 @@ RSpec.describe ::CachingArrayResolver do it 'does not perform a union of a query with itself' do expect(User).to receive(:where).once.and_call_original - [resolve_users(false), resolve_users(false)].each(&method(:force)) + [resolve_users(admin: false), resolve_users(admin: false)].each(&method(:force)) end context 'one of the queries returns no results' do it 'finds the correct values' do - found_admins = resolve_users(true) - found_others = resolve_users(false) - found_all = resolve_users(nil) + found_admins = resolve_users(admin: true) + found_others = resolve_users(admin: false) + found_all = resolve_users(admin: nil) - expect(force(found_admins)).to be_empty - expect(force(found_others)).to match_array(non_admins) - expect(force(found_all)).to match_array(non_admins) + expect(force(found_admins)).to match_array(admins) + expect(force(found_others)).to be_empty + expect(force(found_all)).to match_array(admins) end end context 'one of the queries has already been cached' do before do - force(resolve_users(nil)) + force(resolve_users(admin: nil)) end it 'avoids further queries' do expect do - repeated_find = resolve_users(nil) + repeated_find = resolve_users(admin: 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 end 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 { found: { true => [], false => [], nil => [] } @@ -150,14 +159,14 @@ RSpec.describe ::CachingArrayResolver do end it 'receives item_found for each key the item mapped to' do - found_admins = resolve_users(true, with_item_found) - found_all = resolve_users(nil, with_item_found) + found_admins = resolve_users(admin: true, resolver: with_item_found) + found_all = resolve_users(admin: nil, resolver: with_item_found) [found_admins, found_all].each(&method(:force)) expect(query_context[:found]).to match({ - false => be_empty, true => match_array(admins), + false => be_empty, nil => match_array(admins + non_admins) }) end @@ -167,11 +176,11 @@ RSpec.describe ::CachingArrayResolver do let(:max_page_size) { 2 } it 'respects the max_page_size, on a per subset basis' do - found_all = resolve_users(nil) - found_others = resolve_users(false) + found_all = resolve_users(admin: nil) + found_admins = resolve_users(admin: true) 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 @@ -179,11 +188,11 @@ RSpec.describe ::CachingArrayResolver do let(:max_page_size) { nil } it 'takes the page size from schema.default_max_page_size' do - found_all = resolve_users(nil) - found_others = resolve_users(false) + found_all = resolve_users(admin: nil) + found_admins = resolve_users(admin: true) 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 @@ -197,12 +206,10 @@ RSpec.describe ::CachingArrayResolver do end end - def resolve_users(is_admin, resolver = caching_resolver) - args = { is_admin: is_admin } - resolve(resolver, args: args, field: field, ctx: query_context, schema: schema) - end - - def force(lazy) - ::Gitlab::Graphql::Lazy.force(lazy) + def resolve_users(admin:, resolver: caching_resolver) + args = { is_admin: admin } + 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 diff --git a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb index ebea9e5522b..27ac1572cab 100644 --- a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb +++ b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb @@ -14,7 +14,7 @@ RSpec.describe LooksAhead do # Simplified schema to test lookahead let_it_be(:schema) do - issues_resolver = Class.new(Resolvers::BaseResolver) do + issues_resolver = Class.new(GraphQL::Schema::Resolver) do include LooksAhead def resolve_with_lookahead(**args) @@ -41,7 +41,6 @@ RSpec.describe LooksAhead do field :issues, issue.connection_type, null: true field :issues_with_lookahead, issue.connection_type, - extras: [:lookahead], resolver: issues_resolver, null: true end diff --git a/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb index 850b9f8cc87..cc7e2f6814a 100644 --- a/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver 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), filename: design.filename } @@ -50,7 +50,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do 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 let(:args) { all_singular_args.slice(arg) } @@ -71,7 +71,7 @@ RSpec.describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do describe 'attempting to retrieve an object not visible at this version' do 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 let(:args) { all_singular_args.slice(arg) } 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 403261fc22a..b0fc78af2af 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 @@ -32,7 +32,7 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do end 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) } end @@ -44,13 +44,13 @@ RSpec.describe Resolvers::DesignManagement::VersionInCollectionResolver do end 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 } end 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 } it 'raises an appropriate error' do diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb index 5bc1c555e9a..23d4d86c79a 100644 --- a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb +++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do end 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 } @@ -39,7 +39,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do context 'without constraints' do it 'returns the ordered versions' do - expect(result).to eq(all_versions) + expect(result.to_a).to eq(all_versions) end end @@ -51,13 +51,13 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do end 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' end 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' end @@ -68,8 +68,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do # return successfully let(:params) do { - sha: first_version.sha, - id: global_id_of(first_version) + earlier_or_equal_to_sha: first_version.sha, + earlier_or_equal_to_id: global_id_of(first_version) } end @@ -79,8 +79,8 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do context 'and they do not match' do let(:params) do { - sha: first_version.sha, - id: global_id_of(other_version) + earlier_or_equal_to_sha: first_version.sha, + earlier_or_equal_to_id: global_id_of(other_version) } end @@ -111,7 +111,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do end 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 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 edca11f40d7..170a602fb0d 100644 --- a/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb +++ b/spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb @@ -88,8 +88,8 @@ RSpec.describe Resolvers::ErrorTracking::SentryErrorsResolver do it 'sets the pagination variables' do result = resolve_errors - expect(result.next_cursor).to eq 'next' - expect(result.previous_cursor).to eq 'prev' + expect(result.end_cursor).to eq 'next' + expect(result.start_cursor).to eq 'prev' end it 'returns an externally paginated array' do diff --git a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb index 16eb190efc6..decc3569d6c 100644 --- a/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb +++ b/spec/graphql/resolvers/issue_status_counts_resolver_spec.rb @@ -62,22 +62,13 @@ RSpec.describe Resolvers::IssueStatusCountsResolver do end 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.opened).to eq 0 expect(result.closed).to eq 1 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 def resolve_issue_status_counts(args = {}, context = { current_user: current_user }) diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb index 43cbd4d2bdd..f6f746a8572 100644 --- a/spec/graphql/resolvers/issues_resolver_spec.rb +++ b/spec/graphql/resolvers/issues_resolver_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Resolvers::IssuesResolver do include GraphqlHelpers - let(:current_user) { create(:user) } + let_it_be(:current_user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, group: group) } @@ -25,7 +25,7 @@ RSpec.describe Resolvers::IssuesResolver do end context "with a project" do - before do + before_all do project.add_developer(current_user) create(:label_link, label: label1, target: issue1) create(:label_link, label: label1, target: issue2) @@ -43,11 +43,11 @@ RSpec.describe Resolvers::IssuesResolver do end it 'filters by milestone' do - expect(resolve_issues(milestone_title: milestone.title)).to contain_exactly(issue1) + expect(resolve_issues(milestone_title: [milestone.title])).to contain_exactly(issue1) end 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 it 'filters by two assignees' do @@ -112,15 +112,19 @@ RSpec.describe Resolvers::IssuesResolver do describe 'filters by issue_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 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 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 @@ -143,44 +147,44 @@ RSpec.describe Resolvers::IssuesResolver do describe 'sorting' do context 'when sorting by created' 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 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 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_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_issue4) { create(:issue, project: project, due_date: nil) } 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 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 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_issue2) { create(:issue, project: project, relative_position: nil) } let_it_be(:relative_issue3) { create(:issue, project: project, relative_position: 1000) } let_it_be(:relative_issue4) { create(:issue, project: project, relative_position: nil) } 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 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(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } let_it_be(:priority_label1) { create(:label, project: project, priority: 1) } @@ -200,7 +204,7 @@ RSpec.describe Resolvers::IssuesResolver do end 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(:label2) { create(:label, project: project, priority: 5) } let_it_be(:label3) { create(:label, project: project, priority: 10) } @@ -219,7 +223,7 @@ RSpec.describe Resolvers::IssuesResolver do end 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(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } let_it_be(:milestone_issue1) { create(:issue, project: project) } @@ -236,17 +240,17 @@ RSpec.describe Resolvers::IssuesResolver do end 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_low_severity) { create_issue_with_severity(project, severity: :low) } let_it_be(:issue_no_severity) { create(:incident, project: project) } 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 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 @@ -267,7 +271,9 @@ RSpec.describe Resolvers::IssuesResolver do it 'batches queries that only include IIDs', :request_store 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 expect(result).to contain_exactly(issue1, issue2) diff --git a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb index deb5ff584cf..84ef906b72f 100644 --- a/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Resolvers::MergeRequestPipelinesResolver do end def resolve_pipelines - resolve(described_class, obj: merge_request, ctx: { current_user: current_user }) + sync(resolve(described_class, obj: merge_request, ctx: { current_user: current_user })) end it 'resolves only MRs for the passed merge request' do diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb index 3a3393a185c..63fbd04848d 100644 --- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb @@ -75,9 +75,9 @@ RSpec.describe Resolvers::MergeRequestsResolver do it 'can batch-resolve merge requests from different projects' do # 2 queries for project_authorizations, and 2 for merge_requests result = batch_sync(max_queries: queries_per_project * 2) do - resolve_mr(project, iids: iid_1) + - resolve_mr(project, iids: iid_2) + - resolve_mr(other_project, iids: other_iid) + resolve_mr(project, iids: [iid_1]) + + resolve_mr(project, iids: [iid_2]) + + resolve_mr(other_project, iids: [other_iid]) end expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) @@ -110,7 +110,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do context 'by source branches' 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) end @@ -118,7 +118,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do it 'takes more than one argument' do mrs = [merge_request_3, merge_request_4] 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) end @@ -126,7 +126,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do context 'by target branches' 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) end @@ -134,7 +134,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do it 'takes more than one argument' do mrs = [merge_request_3, merge_request_4] 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) end @@ -153,13 +153,13 @@ RSpec.describe Resolvers::MergeRequestsResolver do let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) } 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) end 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) end @@ -201,7 +201,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do it 'requires all filters' do 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) end @@ -236,7 +236,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do end 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 def resolve_mr(project, resolver: described_class, user: current_user, **args) diff --git a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb index bfb3ce91d58..45777aa96e1 100644 --- a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb @@ -8,14 +8,16 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do let_it_be(:project) { create(:project, :repository) } let_it_be(:current_user) { create(:user) } let_it_be(:other_user) { create(:user) } + let_it_be(:reviewer) { create(:user) } - let_it_be(:merge_request_with_author_and_assignee) do + let_it_be(:merge_request) do create(:merge_request, :unique_branches, source_project: project, target_project: project, author: other_user, - assignee: other_user) + assignee: other_user, + reviewers: [reviewer]) end before do @@ -26,7 +28,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do it 'filters merge requests by assignee username' do result = resolve_mr(project, assignee_username: other_user.username) - expect(result).to eq([merge_request_with_author_and_assignee]) + expect(result).to contain_exactly(merge_request) end it 'does not find anything' do @@ -40,7 +42,7 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do it 'filters merge requests by author username' do result = resolve_mr(project, author_username: other_user.username) - expect(result).to eq([merge_request_with_author_and_assignee]) + expect(result).to contain_exactly(merge_request) end it 'does not find anything' do @@ -50,7 +52,21 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do end end - def resolve_mr(project, args, resolver: described_class, user: current_user) + context 'by reviewer' do + it 'filters merge requests by reviewer username' do + result = resolve_mr(project, reviewer_username: reviewer.username) + + expect(result).to contain_exactly(merge_request) + end + + it 'does not find anything' do + result = resolve_mr(project, reviewer_username: 'unknown-user') + + expect(result).to be_empty + end + end + + def resolve_mr(project, resolver: described_class, user: current_user, **args) resolve(resolver, obj: project, args: args, ctx: { current_user: user }) end end diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb index 1950c2ca067..b852b349d4f 100644 --- a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb +++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb @@ -18,6 +18,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do resolve(described_class, obj: project, args: args, ctx: { current_user: current_user }) end + before do + project.add_developer(current_user) + end + it 'resolves pipeline for the passed iid' do result = batch_sync do resolve_pipeline(project, { iid: '1234' }) @@ -26,6 +30,21 @@ RSpec.describe Resolvers::ProjectPipelineResolver do expect(result).to eq(pipeline) end + it 'keeps the queries under the threshold' do + create(:ci_pipeline, project: project, iid: '1235') + + control = ActiveRecord::QueryRecorder.new do + batch_sync { resolve_pipeline(project, { iid: '1234' }) } + end + + expect do + batch_sync do + resolve_pipeline(project, { iid: '1234' }) + resolve_pipeline(project, { iid: '1235' }) + end + end.not_to exceed_query_limit(control) + end + it 'does not resolve a pipeline outside the project' do result = batch_sync do resolve_pipeline(other_pipeline.project, { iid: '1234' }) diff --git a/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb new file mode 100644 index 00000000000..c0367f7d42e --- /dev/null +++ b/spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::ProjectPipelineStatisticsResolver do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + + specify do + expect(described_class).to have_nullable_graphql_type(::Types::Ci::AnalyticsType) + end + + def resolve_statistics(project, args) + resolve(described_class, obj: project, args: args) + end + + describe '#resolve' do + it 'returns the pipelines statistics for a given project' do + result = resolve_statistics(project, {}) + expect(result.keys).to contain_exactly( + :week_pipelines_labels, + :week_pipelines_totals, + :week_pipelines_successful, + :month_pipelines_labels, + :month_pipelines_totals, + :month_pipelines_successful, + :year_pipelines_labels, + :year_pipelines_totals, + :year_pipelines_successful, + :pipeline_times_labels, + :pipeline_times_values + ) + end + end +end diff --git a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb deleted file mode 100644 index ad59cb6b95e..00000000000 --- a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Resolvers::Projects::JiraImportsResolver do - include GraphqlHelpers - - specify do - expect(described_class).to have_nullable_graphql_type(Types::JiraImportType.connection_type) - end - - describe '#resolve' do - let_it_be(:user) { create(:user) } - let_it_be(:project, reload: true) { create(:project, :public) } - - context 'when project does not have Jira imports' do - let(:current_user) { user } - - context 'when user cannot read Jira imports' do - context 'when anonymous user' do - let(:current_user) { nil } - - it_behaves_like 'no Jira import access' - end - end - - context 'when user can read Jira import data' do - before do - project.add_guest(user) - end - - it_behaves_like 'no Jira import data present' - - it 'does not raise access error' do - expect do - resolve_imports - end.not_to raise_error - end - end - end - - context 'when project has Jira imports' do - let_it_be(:current_user) { user } - let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) } - let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) } - - context 'when user cannot read Jira imports' do - context 'when anonymous user' do - let(:current_user) { nil } - - it_behaves_like 'no Jira import access' - end - end - - context 'when user can access Jira imports' do - before do - project.add_guest(user) - end - - it 'returns Jira imports sorted ascending by created_at time' do - imports = resolve_imports - - expect(imports.size).to eq 2 - expect(imports.map(&:jira_project_key)).to eq %w(BB AA) - end - end - end - end - - def resolve_imports(args = {}, context = { current_user: current_user }) - resolve(described_class, obj: project, args: args, ctx: context) - end -end diff --git a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb index 6f7feff8fe5..2d8929c0e8f 100644 --- a/spec/graphql/resolvers/projects/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/snippets_resolver_spec.rb @@ -6,16 +6,18 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do include GraphqlHelpers 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(:project) { create(:project) } - let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: current_user) } - let_it_be(:project_snippet) { create(:project_snippet, :internal, author: current_user, project: project) } + let_it_be(:personal_snippet) { create(:personal_snippet, :private, author: user) } + 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) } - before do - project.add_developer(current_user) + let(:current_user) { user } + + before_all do + project.add_developer(user) end it 'calls SnippetsFinder' do @@ -42,20 +44,26 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do end 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) end it 'returns the snippets by array of gid' do 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) 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 @@ -65,8 +73,10 @@ RSpec.describe Resolvers::Projects::SnippetsResolver do end context 'when provided user is not current user' do + let(:current_user) { other_user } + 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 diff --git a/spec/graphql/resolvers/releases_resolver_spec.rb b/spec/graphql/resolvers/releases_resolver_spec.rb index b9b90686aa7..89623be891f 100644 --- a/spec/graphql/resolvers/releases_resolver_spec.rb +++ b/spec/graphql/resolvers/releases_resolver_spec.rb @@ -17,6 +17,7 @@ RSpec.describe Resolvers::ReleasesResolver do let_it_be(:public_user) { create(:user) } let(:args) { { sort: :released_at_desc } } + let(:all_releases) { [release_v1, release_v2, release_v3] } before do project.add_developer(developer) @@ -27,7 +28,7 @@ RSpec.describe Resolvers::ReleasesResolver do let(:current_user) { public_user } it 'returns an empty array' do - expect(resolve_releases).to eq([]) + expect(resolve_releases).to be_empty end end @@ -35,7 +36,7 @@ RSpec.describe Resolvers::ReleasesResolver do let(:current_user) { developer } 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 describe 'sorting behavior' do @@ -43,7 +44,9 @@ RSpec.describe Resolvers::ReleasesResolver do let(:args) { { sort: :released_at_desc } } 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 @@ -51,7 +54,9 @@ RSpec.describe Resolvers::ReleasesResolver do let(:args) { { sort: :released_at_asc } } 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 @@ -59,7 +64,9 @@ RSpec.describe Resolvers::ReleasesResolver do let(:args) { { sort: :created_desc } } 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 @@ -67,7 +74,9 @@ RSpec.describe Resolvers::ReleasesResolver do let(:args) { { sort: :created_asc } } 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 diff --git a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb index 16e69f662c0..ebe286769cf 100644 --- a/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb +++ b/spec/graphql/resolvers/snippets/blobs_resolver_spec.rb @@ -16,16 +16,18 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do context 'when user is not authorized' do let(:other_user) { create(:user) } - it 'raises an error' do - expect do - resolve_blobs(snippet, user: other_user) - end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + it 'redacts the field' do + expect(resolve_blobs(snippet, user: other_user)).to be_nil end end context 'when using no filter' do it 'returns all snippet blobs' do - expect(resolve_blobs(snippet).map(&:path)).to contain_exactly(*snippet.list_files) + result = resolve_blobs(snippet, args: {}) + + expect(result).to match_array(snippet.list_files.map do |file| + have_attributes(path: file) + end) end end @@ -34,7 +36,13 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do it 'returns an array of files' do path = 'CHANGELOG' - expect(resolve_blobs(snippet, args: { paths: path }).first.path).to eq(path) + expect(resolve_blobs(snippet, paths: [path])).to contain_exactly(have_attributes(path: path)) + end + end + + context 'the argument does not match anything' do + it 'returns an empty result' do + expect(resolve_blobs(snippet, paths: ['does not exist'])).to be_empty end end @@ -42,13 +50,15 @@ RSpec.describe Resolvers::Snippets::BlobsResolver do it 'returns an array of files' do paths = ['CHANGELOG', 'README.md'] - expect(resolve_blobs(snippet, args: { paths: paths }).map(&:path)).to contain_exactly(*paths) + expect(resolve_blobs(snippet, paths: paths)).to match_array(paths.map do |file| + have_attributes(path: file) + end) end end end end - def resolve_blobs(snippet, user: current_user, args: {}) + def resolve_blobs(snippet, user: current_user, paths: [], args: { paths: paths }) resolve(described_class, args: args, ctx: { current_user: user }, obj: snippet) end end diff --git a/spec/graphql/resolvers/snippets_resolver_spec.rb b/spec/graphql/resolvers/snippets_resolver_spec.rb index a58d9c5ac3a..11cb1c0ec4b 100644 --- a/spec/graphql/resolvers/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/snippets_resolver_spec.rb @@ -84,19 +84,18 @@ RSpec.describe Resolvers::SnippetsResolver do end 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) end it 'returns the snippets by array of gid' do - args = { - ids: [personal_snippet.to_global_id, project_snippet.to_global_id] - } + snippets = [personal_snippet, project_snippet] + 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 it 'returns an error if the id cannot be coerced' do diff --git a/spec/graphql/resolvers/todo_resolver_spec.rb b/spec/graphql/resolvers/todo_resolver_spec.rb index c764f389c16..ac14852b365 100644 --- a/spec/graphql/resolvers/todo_resolver_spec.rb +++ b/spec/graphql/resolvers/todo_resolver_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Resolvers::TodoResolver do it 'calls TodosFinder' do expect_next_instance_of(TodosFinder) do |finder| - expect(finder).to receive(:execute) + expect(finder).to receive(:execute).and_call_original end resolve_todos @@ -48,7 +48,7 @@ RSpec.describe Resolvers::TodoResolver do end 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) end diff --git a/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb new file mode 100644 index 00000000000..cc855bbcb53 --- /dev/null +++ b/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::UserDiscussionsCountResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:private_project) { create(:project, :private) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:private_issue) { create(:issue, project: private_project) } + let_it_be(:public_discussions) { create_list(:discussion_note_on_issue, 2, noteable: issue, project: project) } + let_it_be(:system_discussion) { create(:discussion_note_on_issue, system: true, noteable: issue, project: project) } + let_it_be(:private_discussion) { create_list(:discussion_note_on_issue, 3, noteable: private_issue, project: private_project) } + + specify do + expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE) + end + + context 'when counting discussions from a public issue' do + subject { batch_sync { resolve_user_discussions_count(issue) } } + + it 'returns the number of discussions for the issue' do + expect(subject).to eq(2) + end + end + + context 'when a user has permission to view discussions' do + before do + private_project.add_developer(user) + end + + subject { batch_sync { resolve_user_discussions_count(private_issue) } } + + it 'returns the number of non-system discussions for the issue' do + expect(subject).to eq(3) + end + end + + context 'when a user does not have permission to view discussions' do + subject { batch_sync { resolve_user_discussions_count(private_issue) } } + + it 'returns no discussions' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + + def resolve_user_discussions_count(obj) + resolve(described_class, obj: obj, ctx: { current_user: user }) + end +end diff --git a/spec/graphql/resolvers/user_notes_count_resolver_spec.rb b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb new file mode 100644 index 00000000000..3cb0810c698 --- /dev/null +++ b/spec/graphql/resolvers/user_notes_count_resolver_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Resolvers::UserNotesCountResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, :public) } + let_it_be(:private_project) { create(:project, :repository, :private) } + + specify do + expect(described_class).to have_nullable_graphql_type(GraphQL::INT_TYPE) + end + + context 'when counting notes from an issue' do + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:private_issue) { create(:issue, project: private_project) } + let_it_be(:public_notes) { create_list(:note, 2, noteable: issue, project: project) } + let_it_be(:system_note) { create(:note, system: true, noteable: issue, project: project) } + let_it_be(:private_notes) { create_list(:note, 3, noteable: private_issue, project: private_project) } + + context 'when counting notes from a public issue' do + subject { batch_sync { resolve_user_notes_count(issue) } } + + it 'returns the number of non-system notes for the issue' do + expect(subject).to eq(2) + end + end + + context 'when a user has permission to view notes' do + before do + private_project.add_developer(user) + end + + subject { batch_sync { resolve_user_notes_count(private_issue) } } + + it 'returns the number of notes for the issue' do + expect(subject).to eq(3) + end + end + + context 'when a user does not have permission to view notes' do + subject { batch_sync { resolve_user_notes_count(private_issue) } } + + it 'returns no notes' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + + context 'when counting notes from a merge request' do + let_it_be(:merge_request) { create(:merge_request, source_project: project) } + let_it_be(:private_merge_request) { create(:merge_request, source_project: private_project) } + let_it_be(:public_notes) { create_list(:note, 2, noteable: merge_request, project: project) } + let_it_be(:system_note) { create(:note, system: true, noteable: merge_request, project: project) } + let_it_be(:private_notes) { create_list(:note, 3, noteable: private_merge_request, project: private_project) } + + context 'when counting notes from a public merge request' do + subject { batch_sync { resolve_user_notes_count(merge_request) } } + + it 'returns the number of non-system notes for the merge request' do + expect(subject).to eq(2) + end + end + + context 'when a user has permission to view notes' do + before do + private_project.add_developer(user) + end + + subject { batch_sync { resolve_user_notes_count(private_merge_request) } } + + it 'returns the number of notes for the merge request' do + expect(subject).to eq(3) + end + end + + context 'when a user does not have permission to view notes' do + subject { batch_sync { resolve_user_notes_count(private_merge_request) } } + + it 'returns no notes' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + end + end + + def resolve_user_notes_count(obj) + resolve(described_class, obj: obj, ctx: { current_user: user }) + end +end diff --git a/spec/graphql/resolvers/users/snippets_resolver_spec.rb b/spec/graphql/resolvers/users/snippets_resolver_spec.rb index 9ccbebc59e6..11a5b7517e0 100644 --- a/spec/graphql/resolvers/users/snippets_resolver_spec.rb +++ b/spec/graphql/resolvers/users/snippets_resolver_spec.rb @@ -51,24 +51,23 @@ RSpec.describe Resolvers::Users::SnippetsResolver do end 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) end it 'returns the snippets by array of gid' do - args = { - ids: [private_personal_snippet.to_global_id, public_personal_snippet.to_global_id] - } + snippets = [private_personal_snippet, public_personal_snippet] + 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 it 'returns an error if the gid is invalid' do args = { - ids: [private_personal_snippet.to_global_id, 'foo'] + ids: [global_id_of(private_personal_snippet), 'foo'] } expect do |