summaryrefslogtreecommitdiff
path: root/spec/graphql/resolvers
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 11:18:50 +0000
commit8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch)
treea77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/graphql/resolvers
parent00b35af3db1abfe813a778f643dad221aad51fca (diff)
downloadgitlab-ce-8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781.tar.gz
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.rb52
-rw-r--r--spec/graphql/resolvers/concerns/looks_ahead_spec.rb177
-rw-r--r--spec/graphql/resolvers/concerns/resolves_project_spec.rb37
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb157
-rw-r--r--spec/graphql/resolvers/project_members_resolver_spec.rb62
-rw-r--r--spec/graphql/resolvers/project_pipeline_resolver_spec.rb36
-rw-r--r--spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb10
-rw-r--r--spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb81
-rw-r--r--spec/graphql/resolvers/user_resolver_spec.rb45
-rw-r--r--spec/graphql/resolvers/users_resolver_spec.rb51
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