summaryrefslogtreecommitdiff
path: root/spec/graphql
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-18 09:45:46 +0000
commita7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch)
tree7452bd5c3545c2fa67a28aa013835fb4fa071baf /spec/graphql
parentee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff)
downloadgitlab-ce-a7b3560714b4d9cc4ab32dffcd1f74a284b93580.tar.gz
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'spec/graphql')
-rw-r--r--spec/graphql/features/authorization_spec.rb2
-rw-r--r--spec/graphql/graphql_triggers_spec.rb14
-rw-r--r--spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb2
-rw-r--r--spec/graphql/mutations/ci/runner/delete_spec.rb15
-rw-r--r--spec/graphql/mutations/issues/create_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb63
-rw-r--r--spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb49
-rw-r--r--spec/graphql/resolvers/ci/runners_resolver_spec.rb111
-rw-r--r--spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/clusters/agents_resolver_spec.rb10
-rw-r--r--spec/graphql/resolvers/merge_requests_resolver_spec.rb60
-rw-r--r--spec/graphql/resolvers/package_details_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/package_pipelines_resolver_spec.rb7
-rw-r--r--spec/graphql/resolvers/recent_boards_resolver_spec.rb79
-rw-r--r--spec/graphql/types/ci/pipeline_counts_type_spec.rb87
-rw-r--r--spec/graphql/types/ci/runner_type_spec.rb3
-rw-r--r--spec/graphql/types/clusters/agent_activity_event_type_spec.rb2
-rw-r--r--spec/graphql/types/clusters/agent_token_type_spec.rb2
-rw-r--r--spec/graphql/types/clusters/agent_type_spec.rb2
-rw-r--r--spec/graphql/types/global_id_type_spec.rb2
-rw-r--r--spec/graphql/types/group_type_spec.rb1
-rw-r--r--spec/graphql/types/issuable_type_spec.rb6
-rw-r--r--spec/graphql/types/member_interface_spec.rb13
-rw-r--r--spec/graphql/types/project_type_spec.rb12
-rw-r--r--spec/graphql/types/repository/blob_type_spec.rb5
-rw-r--r--spec/graphql/types/root_storage_statistics_type_spec.rb2
-rw-r--r--spec/graphql/types/subscription_type_spec.rb1
-rw-r--r--spec/graphql/types/user_preferences_type_spec.rb15
-rw-r--r--spec/graphql/types/user_type_spec.rb36
29 files changed, 535 insertions, 78 deletions
diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb
index faf19104731..514f63a6f5a 100644
--- a/spec/graphql/features/authorization_spec.rb
+++ b/spec/graphql/features/authorization_spec.rb
@@ -326,7 +326,7 @@ RSpec.describe 'DeclarativePolicy authorization in GraphQL ' do
let!(:other_project) { create(:project, :private) }
let!(:visible_issues) { create_list(:issue, 2, project: visible_project) }
let!(:other_issues) { create_list(:issue, 2, project: other_project) }
- let!(:user) { visible_project.owner }
+ let!(:user) { visible_project.first_owner }
let(:issue_type) do
type_factory do |type|
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb
index 0b53c633077..2d83edca363 100644
--- a/spec/graphql/graphql_triggers_spec.rb
+++ b/spec/graphql/graphql_triggers_spec.rb
@@ -17,4 +17,18 @@ RSpec.describe GraphqlTriggers do
GraphqlTriggers.issuable_assignees_updated(issue)
end
end
+
+ describe '.issuable_title_updated' do
+ it 'triggers the issuableTitleUpdated subscription' do
+ work_item = create(:work_item)
+
+ expect(GitlabSchema.subscriptions).to receive(:trigger).with(
+ 'issuableTitleUpdated',
+ { issuable_id: work_item.to_gid },
+ work_item
+ ).and_call_original
+
+ GraphqlTriggers.issuable_title_updated(work_item)
+ end
+ end
end
diff --git a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
index 8ec99070c91..ea5e21ec4b8 100644
--- a/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
+++ b/spec/graphql/mutations/alert_management/alerts/todo/create_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Mutations::AlertManagement::Alerts::Todo::Create do
let_it_be(:alert) { create(:alert_management_alert) }
let_it_be(:project) { alert.project }
- let(:current_user) { project.owner }
+ let(:current_user) { project.first_owner }
let(:args) { { project_path: project.full_path, iid: alert.iid } }
diff --git a/spec/graphql/mutations/ci/runner/delete_spec.rb b/spec/graphql/mutations/ci/runner/delete_spec.rb
index 9f30c95edd5..b53ee30f826 100644
--- a/spec/graphql/mutations/ci/runner/delete_spec.rb
+++ b/spec/graphql/mutations/ci/runner/delete_spec.rb
@@ -11,9 +11,7 @@ RSpec.describe Mutations::Ci::Runner::Delete do
let(:current_ctx) { { current_user: user } }
let(:mutation_params) do
- {
- id: runner.to_global_id
- }
+ { id: runner.to_global_id }
end
specify { expect(described_class).to require_graphql_authorizations(:delete_runner) }
@@ -57,6 +55,10 @@ RSpec.describe Mutations::Ci::Runner::Delete do
it 'deletes runner' do
mutation_params[:id] = project_runner.to_global_id
+ expect_next_instance_of(::Ci::UnregisterRunnerService, project_runner) do |service|
+ expect(service).to receive(:execute).once.and_call_original
+ end
+
expect { subject }.to change { Ci::Runner.count }.by(-1)
expect(subject[:errors]).to be_empty
end
@@ -73,6 +75,9 @@ RSpec.describe Mutations::Ci::Runner::Delete do
it 'does not delete project runner' do
mutation_params[:id] = two_projects_runner.to_global_id
+ allow_next_instance_of(::Ci::UnregisterRunnerService) do |service|
+ expect(service).not_to receive(:execute).once
+ end
expect { subject }.not_to change { Ci::Runner.count }
expect(subject[:errors]).to contain_exactly("Runner #{two_projects_runner.to_global_id} associated with more than one project")
end
@@ -84,6 +89,10 @@ RSpec.describe Mutations::Ci::Runner::Delete do
let(:current_ctx) { { current_user: admin_user } }
it 'deletes runner' do
+ expect_next_instance_of(::Ci::UnregisterRunnerService, runner) do |service|
+ expect(service).to receive(:execute).once.and_call_original
+ end
+
expect { subject }.to change { Ci::Runner.count }.by(-1)
expect(subject[:errors]).to be_empty
end
diff --git a/spec/graphql/mutations/issues/create_spec.rb b/spec/graphql/mutations/issues/create_spec.rb
index 825d04ff827..e3094e84703 100644
--- a/spec/graphql/mutations/issues/create_spec.rb
+++ b/spec/graphql/mutations/issues/create_spec.rb
@@ -121,7 +121,7 @@ RSpec.describe Mutations::Issues::Create do
end
context 'when creating an issue as owner' do
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
before do
mutation_params.merge!(special_params)
diff --git a/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb b/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb
new file mode 100644
index 00000000000..07b4a5509b2
--- /dev/null
+++ b/spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::ProjectPipelineCountsResolver do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
+ let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) }
+ let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') }
+ let_it_be(:sha_pipeline) { create(:ci_pipeline, :running, project: project, sha: 'deadbeef') }
+ let_it_be(:on_demand_dast_scan) { create(:ci_pipeline, :success, project: project, source: 'ondemand_dast_scan') }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ describe '#resolve' do
+ it 'counts pipelines' do
+ expect(resolve_pipeline_counts).to have_attributes(
+ all: 6,
+ finished: 3,
+ running: 1,
+ pending: 2
+ )
+ end
+
+ it 'counts by ref' do
+ expect(resolve_pipeline_counts(ref: "awesome-feature")).to have_attributes(
+ all: 1,
+ finished: 0,
+ running: 0,
+ pending: 1
+ )
+ end
+
+ it 'counts by sha' do
+ expect(resolve_pipeline_counts(sha: "deadbeef")).to have_attributes(
+ all: 1,
+ finished: 0,
+ running: 1,
+ pending: 0
+ )
+ end
+
+ it 'counts by source' do
+ expect(resolve_pipeline_counts(source: "ondemand_dast_scan")).to have_attributes(
+ all: 1,
+ finished: 1,
+ running: 0,
+ pending: 0
+ )
+ end
+ end
+
+ def resolve_pipeline_counts(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: project, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb
new file mode 100644
index 00000000000..53b673e255b
--- /dev/null
+++ b/spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::RunnerJobsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:irrelevant_pipeline) { create(:ci_pipeline, project: project) }
+
+ let!(:build_one) { create(:ci_build, :success, name: 'Build One', runner: runner, pipeline: pipeline) }
+ let!(:build_two) { create(:ci_build, :success, name: 'Build Two', runner: runner, pipeline: pipeline) }
+ let!(:build_three) { create(:ci_build, :failed, name: 'Build Three', runner: runner, pipeline: pipeline) }
+ let!(:irrelevant_build) { create(:ci_build, name: 'Irrelevant Build', pipeline: irrelevant_pipeline)}
+
+ let(:args) { {} }
+ let(:runner) { create(:ci_runner, :project, projects: [project]) }
+
+ subject { resolve_jobs(args) }
+
+ describe '#resolve' do
+ context 'with authorized user', :enable_admin_mode do
+ let(:current_user) { create(:user, :admin) }
+
+ context 'with statuses argument' do
+ let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } }
+
+ it { is_expected.to contain_exactly(build_one, build_two) }
+ end
+
+ context 'without statuses argument' do
+ it { is_expected.to contain_exactly(build_one, build_two, build_three) }
+ end
+ end
+
+ context 'with unauthorized user' do
+ let(:current_user) { nil }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ private
+
+ def resolve_jobs(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: runner, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
index df6490df915..9251fbf24d9 100644
--- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
@@ -43,34 +43,99 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
# Only thing we can do is to verify that args from the resolver is correctly transformed to params of the Finder and we return the Finder's result back.
describe 'Allowed query arguments' do
let(:finder) { instance_double(::Ci::RunnersFinder) }
- let(:args) do
- {
- active: true,
- status: 'active',
- type: :instance_type,
- tag_list: ['active_runner'],
- search: 'abc',
- sort: :contacted_asc
- }
+
+ context 'with active filter' do
+ let(:args) do
+ {
+ active: true,
+ status: 'active',
+ type: :instance_type,
+ tag_list: ['active_runner'],
+ search: 'abc',
+ sort: :contacted_asc
+ }
+ end
+
+ let(:expected_params) do
+ {
+ active: true,
+ status_status: 'active',
+ type_type: :instance_type,
+ tag_name: ['active_runner'],
+ preload: { tag_name: nil },
+ search: 'abc',
+ sort: 'contacted_asc'
+ }
+ end
+
+ it 'calls RunnersFinder with expected arguments' do
+ expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder)
+ allow(finder).to receive(:execute).once.and_return([:execute_return_value])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
+ end
+
+ context 'with both active and paused filter' do
+ let(:args) do
+ {
+ active: true,
+ paused: true
+ }
+ end
+
+ let(:expected_params) do
+ {
+ active: false,
+ preload: { tag_name: nil }
+ }
+ end
+
+ it 'calls RunnersFinder with expected arguments' do
+ expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder)
+ allow(finder).to receive(:execute).once.and_return([:execute_return_value])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
end
- let(:expected_params) do
- {
- active: true,
- status_status: 'active',
- type_type: :instance_type,
- tag_name: ['active_runner'],
- preload: { tag_name: nil },
- search: 'abc',
- sort: 'contacted_asc'
- }
+ context 'with paused filter' do
+ let(:args) do
+ { paused: true }
+ end
+
+ let(:expected_params) do
+ {
+ active: false,
+ preload: { tag_name: nil }
+ }
+ end
+
+ it 'calls RunnersFinder with expected arguments' do
+ expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder)
+ allow(finder).to receive(:execute).once.and_return([:execute_return_value])
+
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
end
- it 'calls RunnersFinder with expected arguments' do
- allow(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder)
- allow(finder).to receive(:execute).once.and_return([:execute_return_value])
+ context 'with neither paused or active filters' do
+ let(:args) do
+ {}
+ end
+
+ let(:expected_params) do
+ {
+ preload: { tag_name: nil }
+ }
+ end
+
+ it 'calls RunnersFinder with expected arguments' do
+ expect(::Ci::RunnersFinder).to receive(:new).with(current_user: user, params: expected_params).once.and_return(finder)
+ allow(finder).to receive(:execute).once.and_return([:execute_return_value])
- expect(subject.items.to_a).to eq([:execute_return_value])
+ expect(subject.items.to_a).to eq([:execute_return_value])
+ end
end
end
end
diff --git a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb
index 9b54d466681..866f4ce7b5a 100644
--- a/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb
+++ b/spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do
describe '#resolve' do
let(:agent) { create(:cluster_agent) }
- let(:user) { create(:user, maintainer_projects: [agent.project]) }
+ let(:user) { create(:user, developer_projects: [agent.project]) }
let(:ctx) { Hash(current_user: user) }
let!(:matching_token1) { create(:cluster_agent_token, agent: agent, last_used_at: 5.days.ago) }
@@ -33,7 +33,11 @@ RSpec.describe Resolvers::Clusters::AgentTokensResolver do
end
context 'user does not have permission' do
- let(:user) { create(:user, developer_projects: [agent.project]) }
+ let(:user) { create(:user) }
+
+ before do
+ agent.project.add_reporter(user)
+ end
it { is_expected.to be_empty }
end
diff --git a/spec/graphql/resolvers/clusters/agents_resolver_spec.rb b/spec/graphql/resolvers/clusters/agents_resolver_spec.rb
index 70f40748e1d..152d7fa22c4 100644
--- a/spec/graphql/resolvers/clusters/agents_resolver_spec.rb
+++ b/spec/graphql/resolvers/clusters/agents_resolver_spec.rb
@@ -15,10 +15,14 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do
describe '#resolve' do
let_it_be(:project) { create(:project) }
- let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
- let_it_be(:developer) { create(:user, developer_projects: [project]) }
+ let_it_be(:maintainer) { create(:user, developer_projects: [project]) }
+ let_it_be(:reporter) { create(:user) }
let_it_be(:agents) { create_list(:cluster_agent, 2, project: project) }
+ before do
+ project.add_reporter(reporter)
+ end
+
let(:ctx) { { current_user: current_user } }
subject { resolve_agents }
@@ -32,7 +36,7 @@ RSpec.describe Resolvers::Clusters::AgentsResolver do
end
context 'the current user does not have access to clusters' do
- let(:current_user) { developer }
+ let(:current_user) { reporter }
it 'returns an empty result' do
expect(subject).to be_empty
diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
index 1d0eac30a23..e4eaeb9bc3c 100644
--- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb
@@ -321,7 +321,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
describe 'sorting' do
- let(:mrs) do
+ let_it_be(:mrs) do
[
merge_request_with_milestone, merge_request_6, merge_request_5, merge_request_4,
merge_request_3, merge_request_2, merge_request_1
@@ -363,28 +363,44 @@ RSpec.describe Resolvers::MergeRequestsResolver do
def merged_at(mr)
nils_last(mr.metrics.merged_at)
end
+ end
+
+ context 'when sorting by closed at' do
+ before do
+ merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
+ merge_request_3.metrics.update!(latest_closed_at: 5.days.ago)
+ end
+
+ it 'sorts merge requests ascending' do
+ expect(resolve_mr(project, sort: :closed_at_asc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [closed_at(mr), -mr.id] })
+ end
+
+ it 'sorts merge requests descending' do
+ expect(resolve_mr(project, sort: :closed_at_desc))
+ .to match_array(mrs)
+ .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] })
+ end
+
+ def closed_at(mr)
+ nils_last(mr.metrics.latest_closed_at)
+ end
+ end
+
+ context 'when sorting by title' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:mr1) { create(:merge_request, :unique_branches, title: 'foo', source_project: project) }
+ let_it_be(:mr2) { create(:merge_request, :unique_branches, title: 'bar', source_project: project) }
+ let_it_be(:mr3) { create(:merge_request, :unique_branches, title: 'baz', source_project: project) }
+ let_it_be(:mr4) { create(:merge_request, :unique_branches, title: 'Baz 2', source_project: project) }
+
+ it 'sorts issues ascending' do
+ expect(resolve_mr(project, sort: :title_asc).to_a).to eq [mr2, mr3, mr4, mr1]
+ end
- context 'when sorting by closed at' do
- before do
- merge_request_1.metrics.update!(latest_closed_at: 10.days.ago)
- merge_request_3.metrics.update!(latest_closed_at: 5.days.ago)
- end
-
- it 'sorts merge requests ascending' do
- expect(resolve_mr(project, sort: :closed_at_asc))
- .to match_array(mrs)
- .and be_sorted(->(mr) { [closed_at(mr), -mr.id] })
- end
-
- it 'sorts merge requests descending' do
- expect(resolve_mr(project, sort: :closed_at_desc))
- .to match_array(mrs)
- .and be_sorted(->(mr) { [-closed_at(mr), -mr.id] })
- end
-
- def closed_at(mr)
- nils_last(mr.metrics.latest_closed_at)
- end
+ it 'sorts issues descending' do
+ expect(resolve_mr(project, sort: :title_desc).to_a).to eq [mr1, mr4, mr3, mr2]
end
end
end
diff --git a/spec/graphql/resolvers/package_details_resolver_spec.rb b/spec/graphql/resolvers/package_details_resolver_spec.rb
index d6acb31d4e3..c8ee489a034 100644
--- a/spec/graphql/resolvers/package_details_resolver_spec.rb
+++ b/spec/graphql/resolvers/package_details_resolver_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Resolvers::PackageDetailsResolver do
include GraphqlHelpers
let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:user) { project.owner }
+ let_it_be(:user) { project.first_owner }
let_it_be(:package) { create(:composer_package, project: project) }
describe '#resolve' do
diff --git a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb
index d48d4d8ae01..892dc641201 100644
--- a/spec/graphql/resolvers/package_pipelines_resolver_spec.rb
+++ b/spec/graphql/resolvers/package_pipelines_resolver_spec.rb
@@ -8,15 +8,16 @@ RSpec.describe Resolvers::PackagePipelinesResolver do
let_it_be_with_reload(:package) { create(:package) }
let_it_be(:pipelines) { create_list(:ci_pipeline, 3, project: package.project) }
- let(:user) { package.project.owner }
+ let(:user) { package.project.first_owner }
let(:args) { {} }
describe '#resolve' do
subject { resolve(described_class, obj: package, args: args, ctx: { current_user: user }) }
before do
- package.pipelines = pipelines
- package.save!
+ pipelines.each do |pipeline|
+ create(:package_build_info, package: package, pipeline: pipeline)
+ end
end
it { is_expected.to contain_exactly(*pipelines) }
diff --git a/spec/graphql/resolvers/recent_boards_resolver_spec.rb b/spec/graphql/resolvers/recent_boards_resolver_spec.rb
new file mode 100644
index 00000000000..1afdcd42b4f
--- /dev/null
+++ b/spec/graphql/resolvers/recent_boards_resolver_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::RecentBoardsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+
+ shared_examples_for 'group and project recent boards resolver' do
+ let_it_be(:board1) { create(:board, name: 'One', resource_parent: board_parent) }
+ let_it_be(:board2) { create(:board, name: 'Two', resource_parent: board_parent) }
+
+ before do
+ [board1, board2].each { |board| visit_board(board, board_parent) }
+ end
+
+ it 'calls ::Boards::VisitsFinder' do
+ expect_any_instance_of(::Boards::VisitsFinder) do |finder|
+ expect(finder).to receive(:latest)
+ end
+
+ resolve_recent_boards
+ end
+
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new { resolve_recent_boards }
+
+ board3 = create(:board, resource_parent: board_parent)
+ visit_board(board3, board_parent)
+
+ expect { resolve_recent_boards(args: {}) }.not_to exceed_query_limit(control)
+ end
+
+ it 'returns most recent visited boards' do
+ expect(resolve_recent_boards).to match_array [board2, board1]
+ end
+
+ it 'returns a set number of boards' do
+ stub_const('Board::RECENT_BOARDS_SIZE', 1)
+
+ expect(resolve_recent_boards).to match_array [board2]
+ end
+ end
+
+ describe '#resolve' do
+ context 'when there is no parent' do
+ let_it_be(:board_parent) { nil }
+
+ it 'returns none if parent is nil' do
+ expect(resolve_recent_boards).to eq(Board.none)
+ end
+ end
+
+ context 'when project boards' do
+ let_it_be(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
+
+ it_behaves_like 'group and project recent boards resolver'
+ end
+
+ context 'when group boards' do
+ let_it_be(:board_parent) { create(:group) }
+
+ it_behaves_like 'group and project recent boards resolver'
+ end
+ end
+
+ def resolve_recent_boards(args: {})
+ resolve(described_class, obj: board_parent, args: args, ctx: { current_user: user })
+ end
+
+ def visit_board(board, parent)
+ if parent.is_a?(Group)
+ create(:board_group_recent_visit, group: parent, board: board, user: user)
+ else
+ create(:board_project_recent_visit, project: parent, board: board, user: user)
+ end
+ end
+end
diff --git a/spec/graphql/types/ci/pipeline_counts_type_spec.rb b/spec/graphql/types/ci/pipeline_counts_type_spec.rb
new file mode 100644
index 00000000000..7fdb286d253
--- /dev/null
+++ b/spec/graphql/types/ci/pipeline_counts_type_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PipelineCounts'] do
+ include GraphqlHelpers
+
+ let(:current_user) { create(:user) }
+
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:failed_pipeline) { create(:ci_pipeline, :failed, project: project) }
+ let_it_be(:success_pipeline) { create(:ci_pipeline, :success, project: project) }
+ let_it_be(:ref_pipeline) { create(:ci_pipeline, project: project, ref: 'awesome-feature') }
+ let_it_be(:sha_pipeline) { create(:ci_pipeline, :running, project: project, sha: 'deadbeef') }
+ let_it_be(:on_demand_dast_scan) { create(:ci_pipeline, :success, project: project, source: 'ondemand_dast_scan') }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ specify { expect(described_class.graphql_name).to eq('PipelineCounts') }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ all
+ finished
+ pending
+ running
+ ]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+
+ shared_examples 'pipeline counts query' do |args: "", expected_counts:|
+ let_it_be(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ pipelineCounts#{args} {
+ all
+ finished
+ pending
+ running
+ }
+ }
+ }
+ )
+ end
+
+ subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json }
+
+ it 'returns pipeline counts' do
+ actual_counts = subject.dig('data', 'project', 'pipelineCounts')
+
+ expect(actual_counts).to eq(expected_counts)
+ end
+ end
+
+ it_behaves_like "pipeline counts query", args: "", expected_counts: {
+ "all" => 6,
+ "finished" => 3,
+ "pending" => 2,
+ "running" => 1
+ }
+
+ it_behaves_like "pipeline counts query", args: '(ref: "awesome-feature")', expected_counts: {
+ "all" => 1,
+ "finished" => 0,
+ "pending" => 1,
+ "running" => 0
+ }
+
+ it_behaves_like "pipeline counts query", args: '(sha: "deadbeef")', expected_counts: {
+ "all" => 1,
+ "finished" => 0,
+ "pending" => 0,
+ "running" => 1
+ }
+
+ it_behaves_like "pipeline counts query", args: '(source: "ondemand_dast_scan")', expected_counts: {
+ "all" => 1,
+ "finished" => 1,
+ "pending" => 0,
+ "running" => 0
+ }
+end
diff --git a/spec/graphql/types/ci/runner_type_spec.rb b/spec/graphql/types/ci/runner_type_spec.rb
index 43d8b585d6b..7697cd0ef79 100644
--- a/spec/graphql/types/ci/runner_type_spec.rb
+++ b/spec/graphql/types/ci/runner_type_spec.rb
@@ -9,9 +9,10 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
it 'contains attributes related to a runner' do
expected_fields = %w[
- id description created_at contacted_at maximum_timeout access_level active status
+ id description created_at contacted_at maximum_timeout access_level active paused status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count admin_url edit_admin_url user_permissions executor_name
+ groups projects jobs token_expires_at
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb
index 7773bad749d..cae75485846 100644
--- a/spec/graphql/types/clusters/agent_activity_event_type_spec.rb
+++ b/spec/graphql/types/clusters/agent_activity_event_type_spec.rb
@@ -6,6 +6,6 @@ RSpec.describe GitlabSchema.types['ClusterAgentActivityEvent'] do
let(:fields) { %i[recorded_at kind level user agent_token] }
it { expect(described_class.graphql_name).to eq('ClusterAgentActivityEvent') }
- it { expect(described_class).to require_graphql_authorizations(:admin_cluster) }
+ it { expect(described_class).to require_graphql_authorizations(:read_cluster) }
it { expect(described_class).to have_graphql_fields(fields) }
end
diff --git a/spec/graphql/types/clusters/agent_token_type_spec.rb b/spec/graphql/types/clusters/agent_token_type_spec.rb
index 3f0720cb4b5..1ca6d690c80 100644
--- a/spec/graphql/types/clusters/agent_token_type_spec.rb
+++ b/spec/graphql/types/clusters/agent_token_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgentToken'] do
it { expect(described_class.graphql_name).to eq('ClusterAgentToken') }
- it { expect(described_class).to require_graphql_authorizations(:admin_cluster) }
+ it { expect(described_class).to require_graphql_authorizations(:read_cluster) }
it { expect(described_class).to have_graphql_fields(fields) }
end
diff --git a/spec/graphql/types/clusters/agent_type_spec.rb b/spec/graphql/types/clusters/agent_type_spec.rb
index a1e5952bf73..3f4faccf15d 100644
--- a/spec/graphql/types/clusters/agent_type_spec.rb
+++ b/spec/graphql/types/clusters/agent_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['ClusterAgent'] do
it { expect(described_class.graphql_name).to eq('ClusterAgent') }
- it { expect(described_class).to require_graphql_authorizations(:admin_cluster) }
+ it { expect(described_class).to require_graphql_authorizations(:read_cluster) }
it { expect(described_class).to have_graphql_fields(fields) }
end
diff --git a/spec/graphql/types/global_id_type_spec.rb b/spec/graphql/types/global_id_type_spec.rb
index 4efa3018dad..e7e69cfad9e 100644
--- a/spec/graphql/types/global_id_type_spec.rb
+++ b/spec/graphql/types/global_id_type_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe Types::GlobalIDType do
describe 'executing against the schema' do
let(:query_result) do
- context = { current_user: issue.project.owner }
+ context = { current_user: issue.project.first_owner }
variables = { 'id' => gid }
run_with_clean_state(query, context: context, variables: variables).to_h
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index 0ba322a100a..82703948cea 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe GitlabSchema.types['Group'] do
dependency_proxy_blob_count dependency_proxy_total_size
dependency_proxy_image_prefix dependency_proxy_image_ttl_policy
shared_runners_setting timelogs organizations contacts work_item_types
+ recent_issue_boards
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/issuable_type_spec.rb b/spec/graphql/types/issuable_type_spec.rb
index 992a58f524b..cb18bbe2eab 100644
--- a/spec/graphql/types/issuable_type_spec.rb
+++ b/spec/graphql/types/issuable_type_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['Issuable'] do
it 'returns possible types' do
- expect(described_class.possible_types).to include(Types::IssueType, Types::MergeRequestType)
+ expect(described_class.possible_types).to include(Types::IssueType, Types::MergeRequestType, Types::WorkItemType)
end
describe '.resolve_type' do
@@ -16,6 +16,10 @@ RSpec.describe GitlabSchema.types['Issuable'] do
expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType)
end
+ it 'resolves work items' do
+ expect(described_class.resolve_type(build(:work_item), {})).to eq(Types::WorkItemType)
+ end
+
it 'raises an error for invalid types' do
expect { described_class.resolve_type(build(:user), {}) }.to raise_error 'Unsupported issuable type'
end
diff --git a/spec/graphql/types/member_interface_spec.rb b/spec/graphql/types/member_interface_spec.rb
index 11fd09eb335..8ecaaa46bed 100644
--- a/spec/graphql/types/member_interface_spec.rb
+++ b/spec/graphql/types/member_interface_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe Types::MemberInterface do
updated_at
expires_at
user
+ merge_request_interaction
]
expect(described_class).to have_graphql_fields(*expected_fields)
@@ -40,4 +41,16 @@ RSpec.describe Types::MemberInterface do
end
end
end
+
+ describe '#merge_request_interaction' do
+ subject { described_class.fields['mergeRequestInteraction'] }
+
+ it 'returns the correct type' do
+ is_expected.to have_graphql_type(Types::UserMergeRequestInteractionType)
+ end
+
+ it 'has the correct arguments' do
+ expect(subject.arguments).to have_key('id')
+ end
+ end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 961e12288d4..7433d465b38 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe GitlabSchema.types['Project'] do
only_allow_merge_if_pipeline_succeeds request_access_enabled
only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled
namespace group statistics repository merge_requests merge_request issues
- issue milestones pipelines removeSourceBranchAfterMerge sentryDetailedError snippets
+ issue milestones pipelines removeSourceBranchAfterMerge pipeline_counts sentryDetailedError snippets
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
environment boards jira_import_status jira_imports services releases release
alert_management_alerts alert_management_alert alert_management_alert_status_counts
@@ -35,6 +35,7 @@ RSpec.describe GitlabSchema.types['Project'] do
pipeline_analytics squash_read_only sast_ci_configuration
cluster_agent cluster_agents agent_configurations
ci_template timelogs merge_commit_template squash_commit_template work_item_types
+ recent_issue_boards ci_config_path_or_default
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -299,6 +300,8 @@ RSpec.describe GitlabSchema.types['Project'] do
:merged_before,
:created_after,
:created_before,
+ :updated_after,
+ :updated_before,
:author_username,
:assignee_username,
:reviewer_username,
@@ -309,6 +312,13 @@ RSpec.describe GitlabSchema.types['Project'] do
end
end
+ describe 'pipelineCounts field' do
+ subject { described_class.fields['pipelineCounts'] }
+
+ it { is_expected.to have_graphql_type(Types::Ci::PipelineCountsType) }
+ it { is_expected.to have_graphql_resolver(Resolvers::Ci::ProjectPipelineCountsResolver) }
+ end
+
describe 'snippets field' do
subject { described_class.fields['snippets'] }
diff --git a/spec/graphql/types/repository/blob_type_spec.rb b/spec/graphql/types/repository/blob_type_spec.rb
index 8d845e5d814..565341d15b9 100644
--- a/spec/graphql/types/repository/blob_type_spec.rb
+++ b/spec/graphql/types/repository/blob_type_spec.rb
@@ -29,6 +29,8 @@ RSpec.describe Types::Repository::BlobType do
:blame_path,
:history_path,
:permalink_path,
+ :environment_formatted_external_url,
+ :environment_external_url_for_route_map,
:code_owners,
:simple_viewer,
:rich_viewer,
@@ -39,7 +41,8 @@ RSpec.describe Types::Repository::BlobType do
:ide_edit_path,
:external_storage_url,
:fork_and_edit_path,
- :ide_fork_and_edit_path
+ :ide_fork_and_edit_path,
+ :language
)
end
end
diff --git a/spec/graphql/types/root_storage_statistics_type_spec.rb b/spec/graphql/types/root_storage_statistics_type_spec.rb
index 4fef8f6eafd..7818be6ee02 100644
--- a/spec/graphql/types/root_storage_statistics_type_spec.rb
+++ b/spec/graphql/types/root_storage_statistics_type_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['RootStorageStatistics'] do
it 'has all the required fields' do
expect(described_class).to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size,
:build_artifacts_size, :packages_size, :wiki_size, :snippets_size,
- :pipeline_artifacts_size, :uploads_size)
+ :pipeline_artifacts_size, :uploads_size, :dependency_proxy_size)
end
specify { expect(described_class).to require_graphql_authorizations(:read_statistics) }
diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb
index bf933945a31..593795de004 100644
--- a/spec/graphql/types/subscription_type_spec.rb
+++ b/spec/graphql/types/subscription_type_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe GitlabSchema.types['Subscription'] do
expected_fields = %i[
issuable_assignees_updated
issue_crm_contacts_updated
+ issuable_title_updated
]
expect(described_class).to have_graphql_fields(*expected_fields).only
diff --git a/spec/graphql/types/user_preferences_type_spec.rb b/spec/graphql/types/user_preferences_type_spec.rb
new file mode 100644
index 00000000000..fac45443290
--- /dev/null
+++ b/spec/graphql/types/user_preferences_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Types::UserPreferencesType do
+ specify { expect(described_class.graphql_name).to eq('UserPreferences') }
+
+ it 'exposes the expected fields' do
+ expected_fields = %i[
+ issues_sort
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/user_type_spec.rb b/spec/graphql/types/user_type_spec.rb
index 4e3f442dc71..a2fc8f4c954 100644
--- a/spec/graphql/types/user_type_spec.rb
+++ b/spec/graphql/types/user_type_spec.rb
@@ -67,14 +67,14 @@ RSpec.describe GitlabSchema.types['User'] do
)
end
- subject { GitlabSchema.execute(query, context: { current_user: current_user }).as_json.dig('data', 'user', 'name') }
+ subject(:user_name) { GitlabSchema.execute(query, context: { current_user: current_user }).as_json.dig('data', 'user', 'name') }
context 'user requests' do
let(:current_user) { user }
context 'a user' do
it 'returns name' do
- expect(subject).to eq('John Smith')
+ expect(user_name).to eq('John Smith')
end
end
@@ -85,21 +85,39 @@ RSpec.describe GitlabSchema.types['User'] do
let(:current_user) { nil }
it 'returns `****`' do
- expect(subject).to eq('****')
+ expect(user_name).to eq('****')
end
end
- it 'returns `****` for a regular user' do
- expect(subject).to eq('****')
+ context 'when the requester is not a project member' do
+ it 'returns `Project bot` for a non project member in a public project' do
+ expect(user_name).to eq('Project bot')
+ end
+
+ context 'in a private project' do
+ let(:project) { create(:project, :private) }
+
+ it 'returns `****` for a non project member in a private project' do
+ expect(user_name).to eq('****')
+ end
+ end
end
- context 'when requester is a project maintainer' do
+ context 'with a project member' do
before do
- project.add_maintainer(user)
+ project.add_guest(user)
+ end
+
+ it 'returns `Project bot` for a project member' do
+ expect(user_name).to eq('Project bot')
end
- it 'returns name' do
- expect(subject).to eq('Project bot')
+ context 'in a private project' do
+ let(:project) { create(:project, :private) }
+
+ it 'returns `Project bot` for a project member in a private project' do
+ expect(user_name).to eq('Project bot')
+ end
end
end
end