diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 14:18:50 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 14:18:50 +0300 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/graphql/resolvers | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/graphql/resolvers')
-rw-r--r-- | spec/graphql/resolvers/alert_management/alert_resolver_spec.rb (renamed from spec/graphql/resolvers/alert_management_alert_resolver_spec.rb) | 6 | ||||
-rw-r--r-- | spec/graphql/resolvers/base_resolver_spec.rb | 52 | ||||
-rw-r--r-- | spec/graphql/resolvers/concerns/looks_ahead_spec.rb | 177 | ||||
-rw-r--r-- | spec/graphql/resolvers/concerns/resolves_project_spec.rb | 37 | ||||
-rw-r--r-- | spec/graphql/resolvers/merge_requests_resolver_spec.rb | 157 | ||||
-rw-r--r-- | spec/graphql/resolvers/project_members_resolver_spec.rb | 62 | ||||
-rw-r--r-- | spec/graphql/resolvers/project_pipeline_resolver_spec.rb | 36 | ||||
-rw-r--r-- | spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb | 10 | ||||
-rw-r--r-- | spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb | 81 | ||||
-rw-r--r-- | spec/graphql/resolvers/user_resolver_spec.rb | 45 | ||||
-rw-r--r-- | spec/graphql/resolvers/users_resolver_spec.rb | 51 |
11 files changed, 673 insertions, 41 deletions
diff --git a/spec/graphql/resolvers/alert_management_alert_resolver_spec.rb b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb index 971a81a826d..6c12f765e69 100644 --- a/spec/graphql/resolvers/alert_management_alert_resolver_spec.rb +++ b/spec/graphql/resolvers/alert_management/alert_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Resolvers::AlertManagementAlertResolver do +describe Resolvers::AlertManagement::AlertResolver do include GraphqlHelpers let_it_be(:current_user) { create(:user) } @@ -45,11 +45,11 @@ describe Resolvers::AlertManagementAlertResolver do let_it_be(:alert_count_3) { create(:alert_management_alert, project: project, events: 3) } it 'sorts alerts ascending' do - expect(resolve_alerts(sort: :events_count_asc)).to eq [alert_2, alert_1, alert_count_3, alert_count_6] + expect(resolve_alerts(sort: :event_count_asc)).to eq [alert_2, alert_1, alert_count_3, alert_count_6] end it 'sorts alerts descending' do - expect(resolve_alerts(sort: :events_count_desc)).to eq [alert_count_6, alert_count_3, alert_1, alert_2] + expect(resolve_alerts(sort: :event_count_desc)).to eq [alert_count_6, alert_count_3, alert_1, alert_2] end end end diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb index 0a21b2797ee..6c384349577 100644 --- a/spec/graphql/resolvers/base_resolver_spec.rb +++ b/spec/graphql/resolvers/base_resolver_spec.rb @@ -41,9 +41,35 @@ describe Resolvers::BaseResolver do end end + context 'when the resolver returns early' do + let(:resolver) do + Class.new(described_class) do + def ready?(**args) + [false, %w(early return)] + end + + def resolve(**args) + raise 'Should not get here' + end + end + end + + it 'runs correctly in our test framework' do + expect(resolve(resolver)).to contain_exactly('early', 'return') + end + + it 'single selects the first early return value' do + expect(resolve(resolver.single)).to eq('early') + end + + it 'last selects the last early return value' do + expect(resolve(resolver.last)).to eq('return') + end + end + describe '.last' do it 'returns a subclass from the resolver' do - expect(last_resolver.last.superclass).to eq(last_resolver) + expect(last_resolver.last.ancestors).to include(last_resolver) end it 'returns the same subclass every time' do @@ -95,4 +121,28 @@ describe Resolvers::BaseResolver do end end end + + describe '#synchronized_object' do + let(:object) { double(foo: :the_foo) } + + let(:resolver) do + Class.new(described_class) do + def resolve(**args) + [synchronized_object.foo] + end + end + end + + it 'handles raw objects' do + expect(resolve(resolver, obj: object)).to contain_exactly(:the_foo) + end + + it 'handles lazy objects' do + delayed = BatchLoader::GraphQL.for(1).batch do |_, loader| + loader.call(1, object) + end + + expect(resolve(resolver, obj: delayed)).to contain_exactly(:the_foo) + end + end end diff --git a/spec/graphql/resolvers/concerns/looks_ahead_spec.rb b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb new file mode 100644 index 00000000000..8b83f887846 --- /dev/null +++ b/spec/graphql/resolvers/concerns/looks_ahead_spec.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe LooksAhead do + include GraphqlHelpers + + let_it_be(:the_user) { create(:user) } + let_it_be(:label_a) { create(:label) } + let_it_be(:label_b) { create(:label) } + let_it_be(:issue_a) { create(:issue, author: the_user, labels: [label_a, label_b]) } + let_it_be(:issue_b) { create(:issue, author: the_user, labels: [label_a]) } + let_it_be(:issue_c) { create(:issue, author: the_user, labels: [label_b]) } + + # Simplified schema to test lookahead + let_it_be(:schema) do + issues_resolver = Class.new(Resolvers::BaseResolver) do + include LooksAhead + + def resolve_with_lookahead(**args) + apply_lookahead(object.issues) + end + + def preloads + { labels: [:labels] } + end + end + + label = Class.new(GraphQL::Schema::Object) do + graphql_name 'Label' + field :id, Integer, null: false + end + issue = Class.new(GraphQL::Schema::Object) do + graphql_name 'Issue' + field :title, String, null: true + field :labels, label.connection_type, null: true + end + user = Class.new(GraphQL::Schema::Object) do + graphql_name 'User' + field :name, String, null: true + field :issues, issue.connection_type, + null: true + field :issues_with_lookahead, issue.connection_type, + extras: [:lookahead], + resolver: issues_resolver, + null: true + end + + Class.new(GraphQL::Schema) do + query(Class.new(GraphQL::Schema::Object) do + graphql_name 'Query' + field :find_user, user, null: true do + argument :username, String, required: true + end + + def find_user(username:) + context[:user_db].find { |u| u.username == username } + end + end) + end + end + + def query(doc = document) + GraphQL::Query.new(schema, + document: doc, + context: { user_db: [the_user] }, + variables: { username: the_user.username }) + end + + let(:document) do + GraphQL.parse <<-GRAPHQL + query($username: String!){ + findUser(username: $username) { + name + issues { + nodes { + title + labels { nodes { id } } + } + } + issuesWithLookahead { + nodes { + title + labels { nodes { id } } + } + } + } + } + GRAPHQL + end + + def run_query(gql_query) + query(GraphQL.parse(gql_query)).result + end + + shared_examples 'a working query on the test schema' do + it 'has a good test setup', :aggregate_failures do + expected_label_ids = [label_a, label_b].cycle.take(4).map(&:id) + issue_titles = [issue_a, issue_b, issue_c].map(&:title) + + res = query.result + + expect(res['errors']).to be_blank + expect(res.dig('data', 'findUser', 'name')).to eq(the_user.name) + %w(issues issuesWithLookahead).each do |field| + expect(all_issue_titles(res, field)).to match_array(issue_titles) + expect(all_label_ids(res, field)).to match_array(expected_label_ids) + end + end + end + + it_behaves_like 'a working query on the test schema' + + it 'preloads labels on issues' do + expect(the_user.issues).to receive(:preload).with(:labels) + + query.result + end + + context 'the feature flag is off' do + before do + stub_feature_flags(described_class::FEATURE_FLAG => false) + end + + it_behaves_like 'a working query on the test schema' + + it 'does not preload labels on issues' do + expect(the_user.issues).not_to receive(:preload).with(:labels) + + query.result + end + end + + it 'issues fewer queries than the naive approach' do + the_user.reload # ensure no attributes are loaded before we begin + naive = <<-GQL + query($username: String!){ + findUser(username: $username) { + name + issues { + nodes { + labels { nodes { id } } + } + } + } + } + GQL + with_lookahead = <<-GQL + query($username: String!){ + findUser(username: $username) { + name + issuesWithLookahead { + nodes { + labels { nodes { id } } + } + } + } + } + GQL + + expect { run_query(with_lookahead) }.to issue_fewer_queries_than { run_query(naive) } + end + + private + + def all_label_ids(result, field_name) + result.dig('data', 'findUser', field_name, 'nodes').flat_map do |node| + node.dig('labels', 'nodes').map { |n| n['id'] } + end + end + + def all_issue_titles(result, field_name) + result.dig('data', 'findUser', field_name, 'nodes').map do |node| + node['title'] + end + end +end diff --git a/spec/graphql/resolvers/concerns/resolves_project_spec.rb b/spec/graphql/resolvers/concerns/resolves_project_spec.rb new file mode 100644 index 00000000000..f29f54483d6 --- /dev/null +++ b/spec/graphql/resolvers/concerns/resolves_project_spec.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ResolvesProject do + include GraphqlHelpers + + let(:implementing_class) do + Class.new do + include ResolvesProject + end + end + + subject(:instance) { implementing_class.new } + + let_it_be(:project) { create(:project) } + + it 'can resolve projects by path' do + expect(sync(instance.resolve_project(full_path: project.full_path))).to eq(project) + end + + it 'can resolve projects by id' do + expect(sync(instance.resolve_project(project_id: global_id_of(project)))).to eq(project) + end + + it 'complains when both are present' do + expect do + instance.resolve_project(full_path: project.full_path, project_id: global_id_of(project)) + end.to raise_error(::Gitlab::Graphql::Errors::ArgumentError) + end + + it 'complains when neither is present' do + expect do + instance.resolve_project(full_path: nil, project_id: nil) + end.to raise_error(::Gitlab::Graphql::Errors::ArgumentError) + end +end diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb index 4217d257ab3..6ff7e1ecac6 100644 --- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb @@ -6,61 +6,164 @@ describe Resolvers::MergeRequestsResolver do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } - let_it_be(:merge_request_1) { create(:merge_request, :simple, source_project: project, target_project: project) } - let_it_be(:merge_request_2) { create(:merge_request, :rebased, source_project: project, target_project: project) } + let_it_be(:current_user) { create(:user) } + let_it_be(:common_attrs) { { author: current_user, source_project: project, target_project: project } } + let_it_be(:merge_request_1) { create(:merge_request, :simple, **common_attrs) } + let_it_be(:merge_request_2) { create(:merge_request, :rebased, **common_attrs) } + let_it_be(:merge_request_3) { create(:merge_request, :unique_branches, **common_attrs) } + let_it_be(:merge_request_4) { create(:merge_request, :unique_branches, :locked, **common_attrs) } + let_it_be(:merge_request_5) { create(:merge_request, :simple, :locked, **common_attrs) } + let_it_be(:merge_request_6) { create(:labeled_merge_request, :unique_branches, labels: create_list(:label, 2), **common_attrs) } let_it_be(:other_project) { create(:project, :repository) } let_it_be(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) } let(:iid_1) { merge_request_1.iid } let(:iid_2) { merge_request_2.iid } let(:other_iid) { other_merge_request.iid } + before do + project.add_developer(current_user) + end + describe '#resolve' do - it 'batch-resolves by target project full path and individual IID' do - result = batch_sync(max_queries: 2) do - resolve_mr(project, iid: iid_1) + resolve_mr(project, iid: iid_2) + context 'no arguments' do + it 'returns all merge requests' do + result = resolve_mr(project, {}) + + expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6) end - expect(result).to contain_exactly(merge_request_1, merge_request_2) + it 'returns only merge requests that the current user can see' do + result = resolve_mr(project, {}, user: build(:user)) + + expect(result).to be_empty + end end - it 'batch-resolves by target project full path and IIDS' do - result = batch_sync(max_queries: 2) do - resolve_mr(project, iids: [iid_1, iid_2]) + context 'by iid alone' do + it 'batch-resolves by target project full path and individual IID' do + result = batch_sync(max_queries: 2) do + [iid_1, iid_2].map { |iid| resolve_mr_single(project, iid) } + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2) + end + + it 'batch-resolves by target project full path and IIDS' do + result = batch_sync(max_queries: 2) do + resolve_mr(project, iids: [iid_1, iid_2]) + end + + expect(result).to contain_exactly(merge_request_1, merge_request_2) + end + + it 'can batch-resolve merge requests from different projects' do + result = batch_sync(max_queries: 3) do + 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) + end + + it 'resolves an unknown iid to be empty' do + result = batch_sync { resolve_mr_single(project, -1) } + + expect(result).to be_nil end - expect(result).to contain_exactly(merge_request_1, merge_request_2) + it 'resolves empty iids to be empty' do + result = batch_sync { resolve_mr(project, iids: []) } + + expect(result).to be_empty + end + + it 'resolves an unknown project to be nil when single' do + result = batch_sync { resolve_mr_single(nil, iid_1) } + + expect(result).to be_nil + end + + it 'resolves an unknown project to be empty' do + result = batch_sync { resolve_mr(nil, iids: [iid_1]) } + + expect(result).to be_empty + end end - it 'can batch-resolve merge requests from different projects' do - result = batch_sync(max_queries: 3) do - resolve_mr(project, iid: iid_1) + - resolve_mr(project, iid: iid_2) + - resolve_mr(other_project, iid: other_iid) + context 'by source branches' do + it 'takes one argument' do + result = resolve_mr(project, source_branch: [merge_request_3.source_branch]) + + expect(result).to contain_exactly(merge_request_3) end - expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request) + 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 ) + + expect(result).to match_array(mrs) + end end - it 'resolves an unknown iid to be empty' do - result = batch_sync { resolve_mr(project, iid: -1) } + context 'by target branches' do + it 'takes one argument' do + result = resolve_mr(project, target_branch: [merge_request_3.target_branch]) + + expect(result).to contain_exactly(merge_request_3) + end - expect(result.compact).to be_empty + 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 ) + + expect(result.compact).to match_array(mrs) + end end - it 'resolves empty iids to be empty' do - result = batch_sync { resolve_mr(project, iids: []) } + context 'by state' do + it 'takes one argument' do + result = resolve_mr(project, state: 'locked') - expect(result).to be_empty + expect(result).to contain_exactly(merge_request_4, merge_request_5) + end end - it 'resolves an unknown project to be empty' do - result = batch_sync { resolve_mr(nil, iid: iid_1) } + context 'by label' do + let_it_be(:label) { merge_request_6.labels.first } + let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) } - expect(result.compact).to be_empty + it 'takes one argument' do + result = resolve_mr(project, label_name: [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)) + + expect(result).to contain_exactly(merge_request_6) + end + end + + describe 'combinations' 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') + + expect(result.compact).to contain_exactly(merge_request_4) + end end end - def resolve_mr(project, args) - resolve(described_class, obj: project, args: args) + def resolve_mr_single(project, iid) + resolve_mr(project, { iids: iid }, resolver: described_class.single) + end + + def resolve_mr(project, args, resolver: described_class, user: current_user) + resolve(resolver, obj: project, args: args, ctx: { current_user: user }) end end diff --git a/spec/graphql/resolvers/project_members_resolver_spec.rb b/spec/graphql/resolvers/project_members_resolver_spec.rb new file mode 100644 index 00000000000..3209838850b --- /dev/null +++ b/spec/graphql/resolvers/project_members_resolver_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::ProjectMembersResolver do + include GraphqlHelpers + + context "with a group" do + let_it_be(:root_group) { create(:group) } + let_it_be(:group_1) { create(:group, parent: root_group) } + let_it_be(:group_2) { create(:group, parent: root_group) } + let_it_be(:project) { create(:project, :public, group: group_1) } + + let_it_be(:user_1) { create(:user, name: 'test user') } + let_it_be(:user_2) { create(:user, name: 'test user 2') } + let_it_be(:user_3) { create(:user, name: 'another user 1') } + let_it_be(:user_4) { create(:user, name: 'another user 2') } + + let_it_be(:project_member) { create(:project_member, user: user_1, project: project) } + let_it_be(:group_1_member) { create(:group_member, user: user_2, group: group_1) } + let_it_be(:group_2_member) { create(:group_member, user: user_3, group: group_2) } + let_it_be(:root_group_member) { create(:group_member, user: user_4, group: root_group) } + + let(:args) { {} } + + subject do + resolve(described_class, obj: project, args: args, ctx: { context: user_4 }) + end + + describe '#resolve' do + it 'finds all project members' do + expect(subject).to contain_exactly(project_member, group_1_member, root_group_member) + end + + context 'with search' do + context 'when the search term matches a user' do + let(:args) { { search: 'test' } } + + it 'searches users by user name' do + expect(subject).to contain_exactly(project_member, group_1_member) + end + end + + context 'when the search term does not match any user' do + let(:args) { { search: 'nothing' } } + + it 'is empty' do + expect(subject).to be_empty + end + end + end + + context 'when project is nil' do + let(:project) { nil } + + it 'returns nil' do + expect(subject).to be_empty + end + end + end + end +end diff --git a/spec/graphql/resolvers/project_pipeline_resolver_spec.rb b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb new file mode 100644 index 00000000000..72049f16d7d --- /dev/null +++ b/spec/graphql/resolvers/project_pipeline_resolver_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::ProjectPipelineResolver do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:pipeline) { create(:ci_pipeline, project: project, iid: '1234') } + let_it_be(:other_pipeline) { create(:ci_pipeline) } + let(:current_user) { create(:user) } + + def resolve_pipeline(project, args) + resolve(described_class, obj: project, args: args, ctx: { current_user: current_user }) + end + + it 'resolves pipeline for the passed iid' do + result = batch_sync do + resolve_pipeline(project, { iid: '1234' }) + end + + expect(result).to eq(pipeline) + end + + it 'does not resolve a pipeline outside the project' do + result = batch_sync do + resolve_pipeline(other_pipeline.project, { iid: '1234' }) + end + + expect(result).to be_nil + end + + it 'errors when no iid is passed' do + expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError) + end +end diff --git a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb index 7146bfb441b..9811075a613 100644 --- a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb @@ -40,16 +40,6 @@ describe Resolvers::Projects::JiraImportsResolver do let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) } let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) } - context 'when feature flag disabled' do - let(:current_user) { user } - - before do - stub_feature_flags(jira_issue_import: false) - end - - it_behaves_like 'no Jira import access' - end - context 'when user cannot read Jira imports' do context 'when anonymous user' do let(:current_user) { nil } diff --git a/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb new file mode 100644 index 00000000000..364e2aa6ca8 --- /dev/null +++ b/spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::Projects::JiraProjectsResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + + shared_examples 'no project service access' do + it 'raises error' do + expect do + resolve_jira_projects + end.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'when project has no jira service' do + let_it_be(:jira_service) { nil } + + context 'when user is a maintainer' do + before do + project.add_maintainer(user) + end + + it_behaves_like 'no project service access' + end + end + + context 'when project has jira service' do + let(:jira_service) { create(:jira_service, project: project) } + + context 'when user is a developer' do + before do + project.add_developer(user) + end + + it_behaves_like 'no project service access' + end + + context 'when user is a maintainer' do + before do + project.add_maintainer(user) + end + + context 'when Jira connection is valid' do + include_context 'jira projects request context' + + it 'returns jira projects' do + jira_projects = resolve_jira_projects + project_keys = jira_projects.map(&:key) + project_names = jira_projects.map(&:name) + project_ids = jira_projects.map(&:id) + + expect(jira_projects.size).to eq 2 + expect(project_keys).to eq(%w(EX ABC)) + expect(project_names).to eq(%w(Example Alphabetical)) + expect(project_ids).to eq(%w(10000 10001)) + end + end + + context 'when Jira connection is not valid' do + before do + WebMock.stub_request(:get, 'https://jira.example.com/rest/api/2/project/search?maxResults=50&query=&startAt=0') + .to_raise(JIRA::HTTPError.new(double(message: 'Some failure.'))) + end + + it 'raises failure error' do + expect { resolve_jira_projects }.to raise_error('Jira request error: Some failure.') + end + end + end + end + end + + def resolve_jira_projects(args = {}, context = { current_user: user }) + resolve(described_class, obj: jira_service, args: args, ctx: context) + end +end diff --git a/spec/graphql/resolvers/user_resolver_spec.rb b/spec/graphql/resolvers/user_resolver_spec.rb new file mode 100644 index 00000000000..45a8816bf26 --- /dev/null +++ b/spec/graphql/resolvers/user_resolver_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::UserResolver do + include GraphqlHelpers + + describe '#resolve' do + let_it_be(:user) { create(:user) } + + context 'when neither an ID or a username is provided' do + it 'raises an ArgumentError' do + expect { resolve_user } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError) + end + end + + it 'raises an ArgumentError when both an ID and username are provided' do + expect { resolve_user(id: user.to_global_id, username: user.username) } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError) + end + + context 'by username' do + it 'returns the correct user' do + expect( + resolve_user(username: user.username) + ).to eq(user) + end + end + + context 'by ID' do + it 'returns the correct user' do + expect( + resolve_user(id: user.to_global_id) + ).to eq(user) + end + end + end + + private + + def resolve_user(args = {}) + sync(resolve(described_class, args: args)) + end +end diff --git a/spec/graphql/resolvers/users_resolver_spec.rb b/spec/graphql/resolvers/users_resolver_spec.rb new file mode 100644 index 00000000000..e752500d52f --- /dev/null +++ b/spec/graphql/resolvers/users_resolver_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::UsersResolver do + include GraphqlHelpers + + let_it_be(:user1) { create(:user) } + let_it_be(:user2) { create(:user) } + + describe '#resolve' do + it 'raises an error when read_users_list is not authorized' do + expect(Ability).to receive(:allowed?).with(nil, :read_users_list).and_return(false) + + expect { resolve_users }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + + context 'when no arguments are passed' do + it 'returns all users' do + expect(resolve_users).to contain_exactly(user1, user2) + end + end + + context 'when both ids and usernames are passed ' do + it 'raises an error' do + expect { resolve_users(ids: [user1.to_global_id.to_s], usernames: [user1.username]) } + .to raise_error(Gitlab::Graphql::Errors::ArgumentError) + end + end + + context 'when a set of IDs is passed' do + it 'returns those users' do + expect( + resolve_users(ids: [user1.to_global_id.to_s, user2.to_global_id.to_s]) + ).to contain_exactly(user1, user2) + end + end + + context 'when a set of usernames is passed' do + it 'returns those users' do + expect( + resolve_users(usernames: [user1.username, user2.username]) + ).to contain_exactly(user1, user2) + end + end + end + + def resolve_users(args = {}) + resolve(described_class, args: args) + end +end |