Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/graphql')
-rw-r--r--spec/requests/api/graphql/boards/board_list_issues_query_spec.rb46
-rw-r--r--spec/requests/api/graphql/boards/board_list_query_spec.rb33
-rw-r--r--spec/requests/api/graphql/boards/board_lists_query_spec.rb8
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb95
-rw-r--r--spec/requests/api/graphql/ci/pipelines_spec.rb12
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb50
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb9
-rw-r--r--spec/requests/api/graphql/container_repository/container_repository_details_spec.rb82
-rw-r--r--spec/requests/api/graphql/current_user/todos_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/design_management/delete_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb16
-rw-r--r--spec/requests/api/graphql/mutations/user_callouts/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb87
-rw-r--r--spec/requests/api/graphql/project/cluster_agents_spec.rb35
-rw-r--r--spec/requests/api/graphql/project/jobs_spec.rb56
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb42
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb34
17 files changed, 523 insertions, 88 deletions
diff --git a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
index 241c658441b..6324db0be4a 100644
--- a/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_list_issues_query_spec.rb
@@ -16,6 +16,7 @@ RSpec.describe 'get board lists' do
let(:params) { '' }
let(:board) { }
+ let(:confidential) { false }
let(:board_parent_type) { board_parent.class.to_s.downcase }
let(:board_data) { graphql_data[board_parent_type]['boards']['nodes'][0] }
let(:lists_data) { board_data['lists']['nodes'][0] }
@@ -30,7 +31,7 @@ RSpec.describe 'get board lists' do
nodes {
lists {
nodes {
- issues(filters: {labelName: "#{label2.title}"}, first: 3) {
+ issues(filters: {labelName: "#{label2.title}", confidential: #{confidential}}, first: 3) {
count
nodes {
#{all_graphql_fields_for('issues'.classify)}
@@ -57,14 +58,15 @@ RSpec.describe 'get board lists' do
end
shared_examples 'group and project board list issues query' do
- let!(:board) { create(:board, resource_parent: board_parent) }
- let!(:label_list) { create(:list, board: board, label: label, position: 10) }
- let!(:issue1) { create(:issue, project: issue_project, labels: [label, label2], relative_position: 9) }
- let!(:issue2) { create(:issue, project: issue_project, labels: [label, label2], relative_position: 2) }
- let!(:issue3) { create(:issue, project: issue_project, labels: [label, label2], relative_position: nil) }
- let!(:issue4) { create(:issue, project: issue_project, labels: [label], relative_position: 9) }
- let!(:issue5) { create(:issue, project: issue_project, labels: [label2], relative_position: 432) }
- let!(:issue6) { create(:issue, project: issue_project, labels: [label, label2], relative_position: nil) }
+ let_it_be(:board) { create(:board, resource_parent: board_parent) }
+ let_it_be(:label_list) { create(:list, board: board, label: label, position: 10) }
+ let_it_be(:issue1) { create(:issue, project: issue_project, labels: [label, label2], relative_position: 9) }
+ let_it_be(:issue2) { create(:issue, project: issue_project, labels: [label, label2], relative_position: 2) }
+ let_it_be(:issue3) { create(:issue, project: issue_project, labels: [label, label2], relative_position: nil) }
+ let_it_be(:issue4) { create(:issue, project: issue_project, labels: [label], relative_position: 9) }
+ let_it_be(:issue5) { create(:issue, project: issue_project, labels: [label2], relative_position: 432) }
+ let_it_be(:issue6) { create(:issue, project: issue_project, labels: [label, label2], relative_position: nil) }
+ let_it_be(:issue7) { create(:issue, project: issue_project, labels: [label, label2], relative_position: 5, confidential: true) }
context 'when the user does not have access to the board' do
it 'returns nil' do
@@ -90,23 +92,33 @@ RSpec.describe 'get board lists' do
expect(issue_id).not_to include(issue6.id)
expect(issue3.relative_position).to be_nil
end
+
+ context 'when filtering by confidential' do
+ let(:confidential) { true }
+
+ it 'returns matching issue' do
+ expect(issue_titles).to match_array([issue7.title])
+ expect(issue_relative_positions).not_to include(nil)
+ end
+ end
end
end
describe 'for a project' do
- let(:board_parent) { project }
- let(:label) { project_label }
- let(:label2) { project_label2 }
- let(:issue_project) { project }
+ let_it_be(:board_parent) { project }
+ let_it_be(:label) { project_label }
+ let_it_be(:label2) { project_label2 }
+ let_it_be(:issue_project) { project }
it_behaves_like 'group and project board list issues query'
end
describe 'for a group' do
- let(:board_parent) { group }
- let(:label) { group_label }
- let(:label2) { group_label2 }
- let(:issue_project) { create(:project, :private, group: group) }
+ let_it_be(:board_parent) { group }
+ let_it_be(:label) { group_label }
+ let_it_be(:label2) { group_label2 }
+
+ let_it_be(:issue_project) { create(:project, :private, group: group) }
before do
allow(board_parent).to receive(:multiple_issue_boards_available?).and_return(false)
diff --git a/spec/requests/api/graphql/boards/board_list_query_spec.rb b/spec/requests/api/graphql/boards/board_list_query_spec.rb
index dec7ca715f2..f01f7e87f10 100644
--- a/spec/requests/api/graphql/boards/board_list_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_list_query_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe 'Querying a Board list' do
let_it_be(:list) { create(:list, board: board, label: label) }
let_it_be(:issue1) { create(:issue, project: project, labels: [label]) }
let_it_be(:issue2) { create(:issue, project: project, labels: [label], assignees: [current_user]) }
+ let_it_be(:issue3) { create(:issue, project: project, labels: [label], confidential: true) }
let(:filters) { {} }
let(:query) do
@@ -37,19 +38,33 @@ RSpec.describe 'Querying a Board list' do
it { is_expected.to include({ 'issuesCount' => 2, 'title' => list.title }) }
- context 'with matching issue filters' do
- let(:filters) { { assigneeUsername: current_user.username } }
+ describe 'issue filters' do
+ context 'with matching assignee username issue filters' do
+ let(:filters) { { assigneeUsername: current_user.username } }
- it 'filters issues metadata' do
- is_expected.to include({ 'issuesCount' => 1, 'title' => list.title })
+ it 'filters issues metadata' do
+ is_expected.to include({ 'issuesCount' => 1, 'title' => list.title })
+ end
end
- end
- context 'with unmatching issue filters' do
- let(:filters) { { assigneeUsername: 'foo' } }
+ context 'with unmatching assignee username issue filters' do
+ let(:filters) { { assigneeUsername: 'foo' } }
+
+ it 'filters issues metadata' do
+ is_expected.to include({ 'issuesCount' => 0, 'title' => list.title })
+ end
+ end
+
+ context 'when filtering by confidential' do
+ let(:filters) { { confidential: true } }
+
+ before_all do
+ project.add_developer(current_user)
+ end
- it 'filters issues metadata' do
- is_expected.to include({ 'issuesCount' => 0, 'title' => list.title })
+ it 'filters issues metadata' do
+ is_expected.to include({ 'issuesCount' => 1, 'title' => list.title })
+ end
end
end
end
diff --git a/spec/requests/api/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
index ace8c59e82d..e8fb9daa43b 100644
--- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb
+++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
@@ -109,9 +109,15 @@ RSpec.describe 'get board lists' do
it 'returns the correct list with issue count for matching issue filters' do
label_list = create(:list, board: board, label: label, position: 10)
create(:issue, project: project, labels: [label, label2])
+ create(:issue, project: project, labels: [label, label2], confidential: true)
create(:issue, project: project, labels: [label])
- post_graphql(query(id: global_id_of(label_list), issueFilters: { labelName: label2.title }), current_user: user)
+ post_graphql(
+ query(
+ id: global_id_of(label_list),
+ issueFilters: { labelName: label2.title, confidential: false }
+ ), current_user: user
+ )
aggregate_failures do
list_node = lists_data[0]['node']
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index b2f4801a083..3a1df3525ef 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe 'Query.project.pipeline' do
describe '.stages.groups.jobs' do
let(:pipeline) do
pipeline = create(:ci_pipeline, project: project, user: user)
- stage = create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'first')
- create(:ci_build, stage_id: stage.id, pipeline: pipeline, name: 'my test job')
+ stage = create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'first', position: 1)
+ create(:ci_build, stage_id: stage.id, pipeline: pipeline, name: 'my test job', scheduling_type: :stage)
pipeline
end
@@ -44,13 +44,23 @@ RSpec.describe 'Query.project.pipeline' do
name
jobs {
nodes {
- detailedStatus {
- id
- }
name
needs {
nodes { #{all_graphql_fields_for('CiBuildNeed')} }
}
+ previousStageJobsOrNeeds {
+ nodes {
+ ... on CiBuildNeed {
+ #{all_graphql_fields_for('CiBuildNeed')}
+ }
+ ... on CiJob {
+ #{all_graphql_fields_for('CiJob')}
+ }
+ }
+ }
+ detailedStatus {
+ id
+ }
pipeline {
id
}
@@ -62,58 +72,61 @@ RSpec.describe 'Query.project.pipeline' do
FIELDS
end
- context 'when there are build needs' do
- before do
- pipeline.statuses.each do |build|
- create_list(:ci_build_need, 2, build: build)
- end
- end
-
- it 'reports the build needs' do
- post_graphql(query, current_user: user)
-
- expect(jobs_graphql_data).to contain_exactly a_hash_including(
- 'needs' => a_hash_including(
- 'nodes' => contain_exactly(
- a_hash_including('name' => String),
- a_hash_including('name' => String)
- )
- )
- )
- end
- end
-
it 'returns the jobs of a pipeline stage' do
post_graphql(query, current_user: user)
expect(jobs_graphql_data).to contain_exactly(a_hash_including('name' => 'my test job'))
end
- describe 'performance' do
+ context 'when there is more than one stage and job needs' do
before do
build_stage = create(:ci_stage_entity, position: 2, name: 'build', project: project, pipeline: pipeline)
test_stage = create(:ci_stage_entity, position: 3, name: 'test', project: project, pipeline: pipeline)
- create(:commit_status, pipeline: pipeline, stage_id: build_stage.id, name: 'docker 1 2')
- create(:commit_status, pipeline: pipeline, stage_id: build_stage.id, name: 'docker 2 2')
- create(:commit_status, pipeline: pipeline, stage_id: test_stage.id, name: 'rspec 1 2')
- create(:commit_status, pipeline: pipeline, stage_id: test_stage.id, name: 'rspec 2 2')
- end
- it 'can find the first stage' do
- post_graphql(query, current_user: user, variables: first_n.with(1))
+ create(:ci_build, pipeline: pipeline, name: 'docker 1 2', scheduling_type: :stage, stage: build_stage, stage_idx: build_stage.position)
+ create(:ci_build, pipeline: pipeline, name: 'docker 2 2', stage: build_stage, stage_idx: build_stage.position, scheduling_type: :dag)
+ create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', scheduling_type: :stage, stage: test_stage, stage_idx: test_stage.position)
+ test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', scheduling_type: :dag, stage: test_stage, stage_idx: test_stage.position)
- expect(jobs_graphql_data).to contain_exactly(a_hash_including('name' => 'my test job'))
+ create(:ci_build_need, build: test_job, name: 'my test job')
end
- it 'reports the build needs and previous stages with no duplicates', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/346433' do
+ it 'reports the build needs and execution requirements', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/347290' do
post_graphql(query, current_user: user)
expect(jobs_graphql_data).to contain_exactly(
- a_hash_including('name' => 'my test job'),
- a_hash_including('name' => 'docker 1 2'),
- a_hash_including('name' => 'docker 2 2'),
- a_hash_including('name' => 'rspec 1 2'),
- a_hash_including('name' => 'rspec 2 2')
+ a_hash_including(
+ 'name' => 'my test job',
+ 'needs' => { 'nodes' => [] },
+ 'previousStageJobsOrNeeds' => { 'nodes' => [] }
+ ),
+ a_hash_including(
+ 'name' => 'docker 1 2',
+ 'needs' => { 'nodes' => [] },
+ 'previousStageJobsOrNeeds' => { 'nodes' => [
+ a_hash_including( 'name' => 'my test job' )
+ ] }
+ ),
+ a_hash_including(
+ 'name' => 'docker 2 2',
+ 'needs' => { 'nodes' => [] },
+ 'previousStageJobsOrNeeds' => { 'nodes' => [] }
+ ),
+ a_hash_including(
+ 'name' => 'rspec 1 2',
+ 'needs' => { 'nodes' => [] },
+ 'previousStageJobsOrNeeds' => { 'nodes' => [
+ a_hash_including('name' => 'docker 1 2'),
+ a_hash_including('name' => 'docker 2 2')
+ ] }
+ ),
+ a_hash_including(
+ 'name' => 'rspec 2 2',
+ 'needs' => { 'nodes' => [a_hash_including('name' => 'my test job')] },
+ 'previousStageJobsOrNeeds' => { 'nodes' => [
+ a_hash_including('name' => 'my test job' )
+ ] }
+ )
)
end
diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb
index 1f47f678898..95ddd0250e7 100644
--- a/spec/requests/api/graphql/ci/pipelines_spec.rb
+++ b/spec/requests/api/graphql/ci/pipelines_spec.rb
@@ -79,12 +79,13 @@ RSpec.describe 'Query.project(fullPath).pipelines' do
create(:ci_build, pipeline: pipeline, stage_id: other_stage.id, name: 'linux: [baz]')
end
- it 'is null if the user is a guest' do
+ it 'is present if the user has guest access' do
project.add_guest(user)
- post_graphql(query, current_user: user, variables: first_n.with(1))
+ post_graphql(query, current_user: user)
- expect(graphql_data_at(:project, :pipelines, :nodes)).to contain_exactly a_hash_including('stages' => be_nil)
+ expect(graphql_data_at(:project, :pipelines, :nodes, :stages, :nodes, :name))
+ .to contain_exactly(eq(stage.name), eq(other_stage.name))
end
it 'is present if the user has reporter access' do
@@ -113,12 +114,13 @@ RSpec.describe 'Query.project(fullPath).pipelines' do
wrap_fields(query_graphql_path(query_path, :name))
end
- it 'is empty if the user is a guest' do
+ it 'is present if the user has guest access' do
project.add_guest(user)
post_graphql(query, current_user: user)
- expect(graphql_data_at(:project, :pipelines, :nodes, :stages, :nodes, :groups)).to be_empty
+ expect(graphql_data_at(:project, :pipelines, :nodes, :stages, :nodes, :groups, :nodes, :name))
+ .to contain_exactly('linux', 'linux')
end
it 'is present if the user has reporter access' do
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index ab53ff654e9..98d3a3b1c51 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -63,7 +63,7 @@ RSpec.describe 'Query.runner(id)' do
'revision' => runner.revision,
'locked' => false,
'active' => runner.active,
- 'status' => runner.status.to_s.upcase,
+ 'status' => runner.status('14.5').to_s.upcase,
'maximumTimeout' => runner.maximum_timeout,
'accessLevel' => runner.access_level.to_s.upcase,
'runUntagged' => runner.run_untagged,
@@ -221,6 +221,54 @@ RSpec.describe 'Query.runner(id)' do
end
end
+ describe 'for runner with status' do
+ let_it_be(:stale_runner) { create(:ci_runner, description: 'Stale runner 1', created_at: 3.months.ago) }
+ let_it_be(:never_contacted_instance_runner) { create(:ci_runner, description: 'Missing runner 1', created_at: 1.month.ago, contacted_at: nil) }
+
+ let(:status_fragment) do
+ %(
+ status
+ legacyStatusWithExplicitVersion: status(legacyMode: "14.5")
+ newStatus: status(legacyMode: null)
+ )
+ end
+
+ let(:query) do
+ %(
+ query {
+ staleRunner: runner(id: "#{stale_runner.to_global_id}") { #{status_fragment} }
+ pausedRunner: runner(id: "#{inactive_instance_runner.to_global_id}") { #{status_fragment} }
+ neverContactedInstanceRunner: runner(id: "#{never_contacted_instance_runner.to_global_id}") { #{status_fragment} }
+ }
+ )
+ end
+
+ it 'retrieves status fields with expected values' do
+ post_graphql(query, current_user: user)
+
+ stale_runner_data = graphql_data_at(:stale_runner)
+ expect(stale_runner_data).to match a_hash_including(
+ 'status' => 'NOT_CONNECTED',
+ 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED',
+ 'newStatus' => 'STALE'
+ )
+
+ paused_runner_data = graphql_data_at(:paused_runner)
+ expect(paused_runner_data).to match a_hash_including(
+ 'status' => 'PAUSED',
+ 'legacyStatusWithExplicitVersion' => 'PAUSED',
+ 'newStatus' => 'OFFLINE'
+ )
+
+ never_contacted_instance_runner_data = graphql_data_at(:never_contacted_instance_runner)
+ expect(never_contacted_instance_runner_data).to match a_hash_including(
+ 'status' => 'NOT_CONNECTED',
+ 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED',
+ 'newStatus' => 'NEVER_CONTACTED'
+ )
+ end
+ end
+
describe 'for multiple runners' do
let_it_be(:project1) { create(:project, :test_repo) }
let_it_be(:project2) { create(:project, :test_repo) }
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 51a07e60e15..267dd1b5e6f 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -62,6 +62,15 @@ RSpec.describe 'Query.runners' do
it_behaves_like 'a working graphql query returning expected runner'
end
+
+ context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do
+ let(:runner_type) { 'PROJECT_TYPE' }
+ let(:status) { 'NEVER_CONTACTED' }
+
+ let!(:expected_runner) { project_runner }
+
+ it_behaves_like 'a working graphql query returning expected runner'
+ end
end
describe 'pagination' do
diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
index d93afcc0f33..802ab847b3d 100644
--- a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
+++ b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb
@@ -30,6 +30,14 @@ RSpec.describe 'container repository details' do
subject { post_graphql(query, current_user: user, variables: variables) }
+ shared_examples 'returning an invalid value error' do
+ it 'returns an error' do
+ subject
+
+ expect(graphql_errors.first.dig('message')).to match(/invalid value/)
+ end
+ end
+
it_behaves_like 'a working graphql query' do
before do
subject
@@ -138,6 +146,80 @@ RSpec.describe 'container repository details' do
end
end
+ context 'sorting the tags' do
+ let(:sort) { 'NAME_DESC' }
+ let(:tags_response) { container_repository_details_response.dig('tags', 'edges') }
+ let(:variables) do
+ { id: container_repository_global_id, n: sort }
+ end
+
+ let(:query) do
+ <<~GQL
+ query($id: ID!, $n: ContainerRepositoryTagSort) {
+ containerRepository(id: $id) {
+ tags(sort: $n) {
+ edges {
+ node {
+ #{all_graphql_fields_for('ContainerRepositoryTag')}
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ it 'sorts the tags', :aggregate_failures do
+ subject
+
+ expect(tags_response.first.dig('node', 'name')).to eq('tag5')
+ expect(tags_response.last.dig('node', 'name')).to eq('latest')
+ end
+
+ context 'invalid sort' do
+ let(:sort) { 'FOO_ASC' }
+
+ it_behaves_like 'returning an invalid value error'
+ end
+ end
+
+ context 'filtering by name' do
+ let(:name) { 'l' }
+ let(:tags_response) { container_repository_details_response.dig('tags', 'edges') }
+ let(:variables) do
+ { id: container_repository_global_id, n: name }
+ end
+
+ let(:query) do
+ <<~GQL
+ query($id: ID!, $n: String) {
+ containerRepository(id: $id) {
+ tags(name: $n) {
+ edges {
+ node {
+ #{all_graphql_fields_for('ContainerRepositoryTag')}
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ it 'sorts the tags', :aggregate_failures do
+ subject
+
+ expect(tags_response.size).to eq(1)
+ expect(tags_response.first.dig('node', 'name')).to eq('latest')
+ end
+
+ context 'invalid filter' do
+ let(:name) { 1 }
+
+ it_behaves_like 'returning an invalid value error'
+ end
+ end
+
context 'with tags with a manifest containing nil fields' do
let(:tags_response) { container_repository_details_response.dig('tags', 'nodes') }
let(:errors) { container_repository_details_response.dig('errors') }
diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb
index 981b10a7467..5a45f0db518 100644
--- a/spec/requests/api/graphql/current_user/todos_query_spec.rb
+++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb
@@ -69,7 +69,7 @@ RSpec.describe 'Query current user todos' do
QUERY
end
- it 'avoids N+1 queries', :request_store do
+ it 'avoids N+1 queries', :request_store, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/338671' do
control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) }
project2 = create(:project)
diff --git a/spec/requests/api/graphql/mutations/design_management/delete_spec.rb b/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
index 1dffb86b344..1f43f113e65 100644
--- a/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
+++ b/spec/requests/api/graphql/mutations/design_management/delete_spec.rb
@@ -53,7 +53,7 @@ RSpec.describe "deleting designs" do
context 'the designs list contains filenames we cannot find' do
it_behaves_like 'a failed request' do
- let(:designs) { %w/foo bar baz/.map { |fn| instance_double('file', filename: fn) } }
+ let(:designs) { %w/foo bar baz/.map { |fn| double('file', filename: fn) } }
let(:the_error) { a_string_matching %r/filenames were not found/ }
end
end
diff --git a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
index 3da702c55d7..2da69509ad6 100644
--- a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Setting issues crm contacts' do
let(:issue) { create(:issue, project: project) }
let(:operation_mode) { Types::MutationOperationModeEnum.default_mode }
- let(:crm_contact_ids) { [global_id_of(contacts[1]), global_id_of(contacts[2])] }
+ let(:contact_ids) { [global_id_of(contacts[1]), global_id_of(contacts[2])] }
let(:does_not_exist_or_no_permission) { "The resource that you are attempting to access does not exist or you don't have permission to perform this action" }
let(:mutation) do
@@ -20,7 +20,7 @@ RSpec.describe 'Setting issues crm contacts' do
project_path: issue.project.full_path,
iid: issue.iid.to_s,
operation_mode: operation_mode,
- crm_contact_ids: crm_contact_ids
+ contact_ids: contact_ids
}
graphql_mutation(:issue_set_crm_contacts, variables,
@@ -83,7 +83,7 @@ RSpec.describe 'Setting issues crm contacts' do
end
context 'append' do
- let(:crm_contact_ids) { [global_id_of(contacts[3])] }
+ let(:contact_ids) { [global_id_of(contacts[3])] }
let(:operation_mode) { Types::MutationOperationModeEnum.enum[:append] }
it 'updates the issue with correct contacts' do
@@ -95,7 +95,7 @@ RSpec.describe 'Setting issues crm contacts' do
end
context 'remove' do
- let(:crm_contact_ids) { [global_id_of(contacts[0])] }
+ let(:contact_ids) { [global_id_of(contacts[0])] }
let(:operation_mode) { Types::MutationOperationModeEnum.enum[:remove] }
it 'updates the issue with correct contacts' do
@@ -107,7 +107,7 @@ RSpec.describe 'Setting issues crm contacts' do
end
context 'when the contact does not exist' do
- let(:crm_contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] }
+ let(:contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] }
it 'returns expected error' do
post_graphql_mutation(mutation, current_user: user)
@@ -120,7 +120,7 @@ RSpec.describe 'Setting issues crm contacts' do
context 'when the contact belongs to a different group' do
let(:group2) { create(:group) }
let(:contact) { create(:contact, group: group2) }
- let(:crm_contact_ids) { [global_id_of(contact)] }
+ let(:contact_ids) { [global_id_of(contact)] }
before do
group2.add_reporter(user)
@@ -137,7 +137,7 @@ RSpec.describe 'Setting issues crm contacts' do
context 'when attempting to add more than 6' do
let(:operation_mode) { Types::MutationOperationModeEnum.enum[:append] }
let(:gid) { global_id_of(contacts[0]) }
- let(:crm_contact_ids) { [gid, gid, gid, gid, gid, gid, gid] }
+ let(:contact_ids) { [gid, gid, gid, gid, gid, gid, gid] }
it 'returns expected error' do
post_graphql_mutation(mutation, current_user: user)
@@ -149,7 +149,7 @@ RSpec.describe 'Setting issues crm contacts' do
context 'when trying to remove non-existent contact' do
let(:operation_mode) { Types::MutationOperationModeEnum.enum[:remove] }
- let(:crm_contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] }
+ let(:contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] }
it 'raises expected error' do
post_graphql_mutation(mutation, current_user: user)
diff --git a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
index 716983f01d2..28a46583d2a 100644
--- a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Create a user callout' do
let_it_be(:current_user) { create(:user) }
- let(:feature_name) { ::UserCallout.feature_names.each_key.first }
+ let(:feature_name) { ::Users::Callout.feature_names.each_key.first }
let(:input) do
{
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index 83ea9ff4dc8..a9019a7611a 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'package details' do
include GraphqlHelpers
- let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:project) { create(:project) }
let_it_be(:composer_package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
@@ -68,7 +68,7 @@ RSpec.describe 'package details' do
subject
expect(graphql_data_at(:package, :versions, :nodes, :version)).to be_present
- expect(graphql_data_at(:package, :versions, :nodes, :versions, :nodes)).to be_empty
+ expect(graphql_data_at(:package, :versions, :nodes, :versions, :nodes)).to eq [nil, nil]
end
end
end
@@ -96,4 +96,87 @@ RSpec.describe 'package details' do
expect(graphql_data_at(:b)).to be(nil)
end
end
+
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'returns no packages' do
+ subject
+
+ expect(graphql_data_at(:package)).to be_nil
+ end
+ end
+
+ context 'pipelines field', :aggregate_failures do
+ let(:pipelines) { create_list(:ci_pipeline, 6, project: project) }
+ let(:pipeline_gids) { pipelines.sort_by(&:id).map(&:to_gid).map(&:to_s).reverse }
+
+ before do
+ composer_package.pipelines = pipelines
+ composer_package.save!
+ end
+
+ def run_query(args)
+ pipelines_nodes = <<~QUERY
+ nodes {
+ id
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ }
+ QUERY
+
+ query = graphql_query_for(:package, { id: package_global_id }, query_graphql_field("pipelines", args, pipelines_nodes))
+ post_graphql(query, current_user: user)
+ end
+
+ it 'loads the second page with pagination first correctly' do
+ run_query(first: 2)
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[0..1])
+
+ cursor = graphql_data.dig('package', 'pipelines', 'pageInfo', 'endCursor')
+
+ run_query(first: 2, after: cursor)
+
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[2..3])
+ end
+
+ it 'loads the second page with pagination last correctly' do
+ run_query(last: 2)
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[4..5])
+
+ cursor = graphql_data.dig('package', 'pipelines', 'pageInfo', 'startCursor')
+
+ run_query(last: 2, before: cursor)
+
+ pipeline_ids = graphql_data.dig('package', 'pipelines', 'nodes').pluck('id')
+
+ expect(pipeline_ids).to eq(pipeline_gids[2..3])
+ end
+
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'returns no packages' do
+ run_query(first: 2)
+
+ expect(graphql_data_at(:package)).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/cluster_agents_spec.rb b/spec/requests/api/graphql/project/cluster_agents_spec.rb
index dc7254dd552..585126f3849 100644
--- a/spec/requests/api/graphql/project/cluster_agents_spec.rb
+++ b/spec/requests/api/graphql/project/cluster_agents_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Project.cluster_agents' do
let_it_be(:project) { create(:project, :public) }
let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
- let_it_be(:agents) { create_list(:cluster_agent, 5, project: project) }
+ let_it_be(:agents) { create_list(:cluster_agent, 3, project: project) }
let(:first) { var('Int') }
let(:cluster_agents_fields) { nil }
@@ -105,4 +105,37 @@ RSpec.describe 'Project.cluster_agents' do
})
end
end
+
+ context 'selecting activity events' do
+ let_it_be(:token) { create(:cluster_agent_token, agent: agents.first) }
+ let_it_be(:event) { create(:agent_activity_event, agent: agents.first, agent_token: token, user: current_user) }
+
+ let(:cluster_agents_fields) { [:id, query_nodes(:activity_events, of: 'ClusterAgentActivityEvent', max_depth: 2)] }
+
+ it 'retrieves activity event details' do
+ post_graphql(query, current_user: current_user)
+
+ response = graphql_data_at(:project, :cluster_agents, :nodes, :activity_events, :nodes).first
+
+ expect(response).to include({
+ 'kind' => event.kind,
+ 'level' => event.level,
+ 'recordedAt' => event.recorded_at.iso8601,
+ 'agentToken' => hash_including('name' => token.name),
+ 'user' => hash_including('name' => current_user.name)
+ })
+ end
+
+ it 'preloads associations to prevent N+1 queries' do
+ user = create(:user)
+ token = create(:cluster_agent_token, agent: agents.second)
+ create(:agent_activity_event, agent: agents.second, agent_token: token, user: user)
+
+ post_graphql(query, current_user: current_user)
+
+ expect do
+ post_graphql(query, current_user: current_user)
+ end.to issue_same_number_of_queries_as { post_graphql(query, current_user: current_user, variables: [first.with(1)]) }
+ end
+ end
end
diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb
new file mode 100644
index 00000000000..1a823ede9ac
--- /dev/null
+++ b/spec/requests/api/graphql/project/jobs_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'Query.project.jobs' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:user) { create(:user) }
+
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, user: user)
+ end
+
+ let(:query) do
+ <<~QUERY
+ {
+ project(fullPath: "#{project.full_path}") {
+ jobs {
+ nodes {
+ name
+ previousStageJobsAndNeeds {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
+ build_stage = create(:ci_stage_entity, position: 1, name: 'build', project: project, pipeline: pipeline)
+ test_stage = create(:ci_stage_entity, position: 2, name: 'test', project: project, pipeline: pipeline)
+ create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
+ create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
+ create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
+ test_job = create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 2 2', stage: test_stage)
+ create(:ci_build_need, build: test_job, name: 'docker 1 2')
+
+ post_graphql(query, current_user: user)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: user)
+ end
+
+ create(:ci_build, name: 'test-a', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ test_b_job = create(:ci_build, name: 'test-b', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
+
+ expect do
+ post_graphql(query, current_user: user)
+ end.not_to exceed_all_query_limit(control)
+ end
+end
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index d46ef313563..73e02e2a4b1 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -273,6 +273,48 @@ RSpec.describe 'getting pipeline information nested in a project' do
end
end
+ context 'N+1 queries on pipeline jobs' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:fields) do
+ <<~FIELDS
+ jobs {
+ nodes {
+ previousStageJobsAndNeeds {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ FIELDS
+ end
+
+ it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
+ build_stage = create(:ci_stage_entity, position: 1, name: 'build', project: project, pipeline: pipeline)
+ test_stage = create(:ci_stage_entity, position: 2, name: 'test', project: project, pipeline: pipeline)
+ create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
+ create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
+ create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
+ test_job = create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 2 2', stage: test_stage)
+ create(:ci_build_need, build: test_job, name: 'docker 1 2')
+
+ post_graphql(query, current_user: current_user)
+
+ control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ post_graphql(query, current_user: current_user)
+ end
+
+ create(:ci_build, name: 'test-a', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ test_b_job = create(:ci_build, name: 'test-b', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
+
+ expect do
+ post_graphql(query, current_user: current_user)
+ end.not_to exceed_all_query_limit(control)
+ end
+ end
+
context 'N+1 queries on stages jobs' do
let(:depth) { 5 }
let(:fields) do
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index e44a7efb354..310a8e9fa33 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -143,6 +143,40 @@ RSpec.describe 'getting project information' do
end
end
+ context 'when the user has guest access' do
+ context 'when the project has public pipelines' do
+ before do
+ pipeline = create(:ci_pipeline, project: project)
+ create(:ci_build, project: project, pipeline: pipeline, name: 'a test job')
+ project.add_guest(current_user)
+ end
+
+ it 'shows all jobs' do
+ query = <<~GQL
+ query {
+ project(fullPath: "#{project.full_path}") {
+ jobs {
+ nodes {
+ name
+ stage {
+ name
+ }
+ }
+ }
+ }
+ }
+ GQL
+
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data_at(:project, :jobs, :nodes)).to contain_exactly({
+ 'name' => 'a test job',
+ 'stage' => { 'name' => 'test' }
+ })
+ end
+ end
+ end
+
context 'when the user does not have access to the project' do
it 'returns an empty field' do
post_graphql(query, current_user: current_user)