diff options
Diffstat (limited to 'spec/requests/api/graphql')
273 files changed, 1862 insertions, 1229 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 9bed720c815..2775c3d4c5a 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 @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'get board lists' do +RSpec.describe 'get board lists', feature_category: :team_planning do include GraphqlHelpers let_it_be(:user) { create(:user) } 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 f01f7e87f10..b5ed0fe35d5 100644 --- a/spec/requests/api/graphql/boards/board_list_query_spec.rb +++ b/spec/requests/api/graphql/boards/board_list_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Querying a Board list' do +RSpec.describe 'Querying a Board list', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } 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 ad7df5c9344..2f23e93e2c6 100644 --- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb +++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'get board lists' do +RSpec.describe 'get board lists', feature_category: :team_planning do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/boards/boards_query_spec.rb b/spec/requests/api/graphql/boards/boards_query_spec.rb index 50004e5a8a1..1407034fa5f 100644 --- a/spec/requests/api/graphql/boards/boards_query_spec.rb +++ b/spec/requests/api/graphql/boards/boards_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'get list of boards' do +RSpec.describe 'get list of boards', feature_category: :team_planning do include GraphqlHelpers include_context 'group and project boards query context' diff --git a/spec/requests/api/graphql/ci/application_setting_spec.rb b/spec/requests/api/graphql/ci/application_setting_spec.rb index 156ee550f16..42ab1786fee 100644 --- a/spec/requests/api/graphql/ci/application_setting_spec.rb +++ b/spec/requests/api/graphql/ci/application_setting_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Application Settings' do +RSpec.describe 'getting Application Settings', feature_category: :continuous_integration do include GraphqlHelpers let(:fields) do diff --git a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb index 2dc7b9764fe..0437a30eccd 100644 --- a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb +++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Getting Ci Cd Setting' do +RSpec.describe 'Getting Ci Cd Setting', feature_category: :continuous_integration do include GraphqlHelpers let_it_be_with_reload(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb index 784019ee926..8154f132430 100644 --- a/spec/requests/api/graphql/ci/config_spec.rb +++ b/spec/requests/api/graphql/ci/config_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.ciConfig' do +RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do include GraphqlHelpers include StubRequests diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb index 17133d7ea66..e6d73701b8f 100644 --- a/spec/requests/api/graphql/ci/config_variables_spec.rb +++ b/spec/requests/api/graphql/ci/config_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do +RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)', feature_category: :pipeline_authoring do include GraphqlHelpers include ReactiveCachingHelpers diff --git a/spec/requests/api/graphql/ci/group_variables_spec.rb b/spec/requests/api/graphql/ci/group_variables_spec.rb index 7baf26c7648..51cbb4719f7 100644 --- a/spec/requests/api/graphql/ci/group_variables_spec.rb +++ b/spec/requests/api/graphql/ci/group_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.group(fullPath).ciVariables' do +RSpec.describe 'Query.group(fullPath).ciVariables', feature_category: :pipeline_authoring do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/ci/groups_spec.rb b/spec/requests/api/graphql/ci/groups_spec.rb index d1a4395d2c9..d1588833d8f 100644 --- a/spec/requests/api/graphql/ci/groups_spec.rb +++ b/spec/requests/api/graphql/ci/groups_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Query.project.pipeline.stages.groups' do +RSpec.describe 'Query.project.pipeline.stages.groups', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/ci/instance_variables_spec.rb b/spec/requests/api/graphql/ci/instance_variables_spec.rb index cd6b2de98a1..e0397e17923 100644 --- a/spec/requests/api/graphql/ci/instance_variables_spec.rb +++ b/spec/requests/api/graphql/ci/instance_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.ciVariables' do +RSpec.describe 'Query.ciVariables', feature_category: :pipeline_authoring do include GraphqlHelpers let(:query) do diff --git a/spec/requests/api/graphql/ci/job_artifacts_spec.rb b/spec/requests/api/graphql/ci/job_artifacts_spec.rb index df6e398fbe5..5fcb363d479 100644 --- a/spec/requests/api/graphql/ci/job_artifacts_spec.rb +++ b/spec/requests/api/graphql/ci/job_artifacts_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).pipelines.jobs.artifacts' do +RSpec.describe 'Query.project(fullPath).pipelines.jobs.artifacts', feature_category: :build do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/ci/job_spec.rb b/spec/requests/api/graphql/ci/job_spec.rb index 3721155c71b..8121c5e5c85 100644 --- a/spec/requests/api/graphql/ci/job_spec.rb +++ b/spec/requests/api/graphql/ci/job_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do +RSpec.describe 'Query.project(fullPath).pipelines.job(id)', feature_category: :continuous_integration do include GraphqlHelpers around do |example| diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb index a161c5c98ed..7a1dc614dcf 100644 --- a/spec/requests/api/graphql/ci/jobs_spec.rb +++ b/spec/requests/api/graphql/ci/jobs_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Query.project.pipeline' do +RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } @@ -367,4 +367,65 @@ RSpec.describe 'Query.project.pipeline' do expect_graphql_errors_to_include [/"jobs" field can be requested only for 1 Project\(s\) at a time./] end end + + context 'when batched querying jobs for multiple projects' do + let(:batched) do + [ + { query: query_1 }, + { query: query_2 } + ] + end + + let(:query_1) do + %( + query Page1 { + projects { + nodes { + jobs { + nodes { + name + } + } + } + } + } + ) + end + + let(:query_2) do + %( + query Page2 { + projects { + nodes { + jobs { + nodes { + name + } + } + } + } + } + ) + end + + before do + create_list(:project, 2).each do |project| + project.add_developer(user) + create(:ci_build, project: project) + end + end + + it 'limits the specific field evaluation per query' do + get_multiplex(batched, current_user: user) + + resp = json_response + + expect(resp.first.dig('data', 'projects', 'nodes').first.dig('jobs', 'nodes').first['name']).to eq('test') + expect(resp.first['errors'].first['message']) + .to match(/"jobs" field can be requested only for 1 Project\(s\) at a time./) + expect(resp.second.dig('data', 'projects', 'nodes').first.dig('jobs', 'nodes').first['name']).to eq('test') + expect(resp.second['errors'].first['message']) + .to match(/"jobs" field can be requested only for 1 Project\(s\) at a time./) + end + end end diff --git a/spec/requests/api/graphql/ci/manual_variables_spec.rb b/spec/requests/api/graphql/ci/manual_variables_spec.rb index a15bac2b8bd..921c69e535d 100644 --- a/spec/requests/api/graphql/ci/manual_variables_spec.rb +++ b/spec/requests/api/graphql/ci/manual_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables' do +RSpec.describe 'Query.project(fullPath).pipelines.jobs.manualVariables', feature_category: :pipeline_authoring do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb index 8b8ba09a95c..76adce6ff1b 100644 --- a/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb +++ b/spec/requests/api/graphql/ci/pipeline_schedules_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe 'Query.project.pipelineSchedules' do +RSpec.describe 'Query.project.pipelineSchedules', feature_category: :continuous_integration do include GraphqlHelpers - let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :repository, :public, creator: user, namespace: user.namespace) } let_it_be(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) } let(:pipeline_schedule_graphql_data) { graphql_data_at(:project, :pipeline_schedules, :nodes, 0) } @@ -29,6 +29,8 @@ RSpec.describe 'Query.project.pipelineSchedules' do forTag cron cronTimezone + editPath + variables { nodes { #{all_graphql_fields_for('PipelineScheduleVariable')} } } } QUERY end @@ -61,6 +63,58 @@ RSpec.describe 'Query.project.pipelineSchedules' do expect(pipeline_schedule_graphql_data['refPath']).to eq("/#{project.full_path}/-/commits/#{ref_for_display}") expect(pipeline_schedule_graphql_data['forTag']).to be(false) end + + it 'returns the edit_path for a pipeline schedule' do + edit_path = pipeline_schedule_graphql_data['editPath'] + + expect(edit_path).to eq("/#{project.full_path}/-/pipeline_schedules/#{pipeline_schedule.id}/edit") + end + end + + describe 'variables' do + let!(:env_vars) { create_list(:ci_pipeline_schedule_variable, 5, pipeline_schedule: pipeline_schedule) } + + it 'returns all variables' do + post_graphql(query, current_user: user) + + variables = pipeline_schedule_graphql_data['variables']['nodes'] + expected = env_vars.map do |var| + a_graphql_entity_for(var, :key, :value, variable_type: var.variable_type.upcase) + end + + expect(variables).to match_array(expected) + end + + it 'is N+1 safe on the variables level' do + baseline = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: user) } + + create_list(:ci_pipeline_schedule_variable, 2, pipeline_schedule: pipeline_schedule) + + expect { post_graphql(query, current_user: user) }.not_to exceed_query_limit(baseline) + end + + it 'is N+1 safe on the schedules level' do + baseline = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: user) } + + pipeline_schedule_2 = create(:ci_pipeline_schedule, project: project, owner: user) + create_list(:ci_pipeline_schedule_variable, 2, pipeline_schedule: pipeline_schedule_2) + + expect { post_graphql(query, current_user: user) }.not_to exceed_query_limit(baseline) + end + end + + describe 'permissions' do + let_it_be(:another_user) { create(:user) } + + before do + post_graphql(query, current_user: another_user) + end + + it 'does not return the edit_path for a pipeline schedule for a user that does not have permissions' do + edit_path = pipeline_schedule_graphql_data['editPath'] + + expect(edit_path).to be nil + end end it 'avoids N+1 queries' do diff --git a/spec/requests/api/graphql/ci/pipelines_spec.rb b/spec/requests/api/graphql/ci/pipelines_spec.rb index 948704e8770..9fe71533b5e 100644 --- a/spec/requests/api/graphql/ci/pipelines_spec.rb +++ b/spec/requests/api/graphql/ci/pipelines_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).pipelines' do +RSpec.describe 'Query.project(fullPath).pipelines', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/ci/project_variables_spec.rb b/spec/requests/api/graphql/ci/project_variables_spec.rb index d49a4a7e768..0338b58a0ea 100644 --- a/spec/requests/api/graphql/ci/project_variables_spec.rb +++ b/spec/requests/api/graphql/ci/project_variables_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).ciVariables' do +RSpec.describe 'Query.project(fullPath).ciVariables', feature_category: :pipeline_authoring do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 94c0a3c41bd..ca08e780758 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.runner(id)' do +RSpec.describe 'Query.runner(id)', feature_category: :runner_fleet do include GraphqlHelpers let_it_be(:user) { create(:user, :admin) } @@ -74,34 +74,39 @@ RSpec.describe 'Query.runner(id)' do runner_data = graphql_data_at(:runner) expect(runner_data).not_to be_nil - expect(runner_data).to match a_hash_including( - 'id' => runner.to_global_id.to_s, - 'description' => runner.description, - 'createdAt' => runner.created_at&.iso8601, - 'contactedAt' => runner.contacted_at&.iso8601, - 'version' => runner.version, - 'shortSha' => runner.short_sha, - 'revision' => runner.revision, - 'locked' => false, - 'active' => runner.active, - 'paused' => !runner.active, - 'status' => runner.status('14.5').to_s.upcase, - 'maximumTimeout' => runner.maximum_timeout, - 'accessLevel' => runner.access_level.to_s.upcase, - 'runUntagged' => runner.run_untagged, - 'ipAddress' => runner.ip_address, - 'runnerType' => runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE', - 'executorName' => runner.executor_type&.dasherize, - 'architectureName' => runner.architecture, - 'platformName' => runner.platform, - 'maintenanceNote' => runner.maintenance_note, - 'maintenanceNoteHtml' => + expect(runner_data).to match a_graphql_entity_for( + runner, + description: runner.description, + created_at: runner.created_at&.iso8601, + contacted_at: runner.contacted_at&.iso8601, + version: runner.version, + short_sha: runner.short_sha, + revision: runner.revision, + locked: false, + active: runner.active, + paused: !runner.active, + status: runner.status('14.5').to_s.upcase, + job_execution_status: runner.builds.running.any? ? 'RUNNING' : 'IDLE', + maximum_timeout: runner.maximum_timeout, + access_level: runner.access_level.to_s.upcase, + run_untagged: runner.run_untagged, + ip_address: runner.ip_address, + runner_type: runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE', + executor_name: runner.executor_type&.dasherize, + architecture_name: runner.architecture, + platform_name: runner.platform, + maintenance_note: runner.maintenance_note, + maintenance_note_html: runner.maintainer_note.present? ? a_string_including('<strong>Test maintenance note</strong>') : '', - 'jobCount' => 0, - 'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything), - 'projectCount' => nil, - 'adminUrl' => "http://localhost/admin/runners/#{runner.id}", - 'userPermissions' => { + job_count: runner.builds.count, + jobs: a_hash_including( + "count" => runner.builds.count, + "nodes" => an_instance_of(Array), + "pageInfo" => anything + ), + project_count: nil, + admin_url: "http://localhost/admin/runners/#{runner.id}", + user_permissions: { 'readRunner' => true, 'updateRunner' => true, 'deleteRunner' => true, @@ -129,10 +134,7 @@ RSpec.describe 'Query.runner(id)' do runner_data = graphql_data_at(:runner) expect(runner_data).not_to be_nil - expect(runner_data).to match a_hash_including( - 'id' => runner.to_global_id.to_s, - 'adminUrl' => nil - ) + expect(runner_data).to match a_graphql_entity_for(runner, admin_url: nil) expect(runner_data['tagList']).to match_array runner.tag_list end end @@ -179,6 +181,16 @@ RSpec.describe 'Query.runner(id)' do expect(runner_data).not_to include('tagList') end end + + context 'with build running' do + before do + project = create(:project, :repository) + pipeline = create(:ci_pipeline, project: project) + create(:ci_build, :running, runner: runner, pipeline: pipeline) + end + + it_behaves_like 'runner details fetch' + end end describe 'for project runner' do @@ -216,9 +228,47 @@ RSpec.describe 'Query.runner(id)' do runner_data = graphql_data_at(:runner) - expect(runner_data).to match a_hash_including( - 'id' => project_runner.to_global_id.to_s, - 'locked' => is_locked + expect(runner_data).to match a_graphql_entity_for(project_runner, locked: is_locked) + end + end + end + + describe 'jobCount' do + let_it_be(:pipeline1) { create(:ci_pipeline, project: project1) } + let_it_be(:pipeline2) { create(:ci_pipeline, project: project1) } + let_it_be(:build1) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline1) } + let_it_be(:build2) { create(:ci_build, :running, runner: active_project_runner, pipeline: pipeline2) } + + let(:runner_query_fragment) { 'id jobCount' } + let(:query) do + %( + query { + runner1: runner(id: "#{active_project_runner.to_global_id}") { #{runner_query_fragment} } + runner2: runner(id: "#{inactive_instance_runner.to_global_id}") { #{runner_query_fragment} } + } + ) + end + + it 'retrieves correct jobCount values' do + post_graphql(query, current_user: user) + + expect(graphql_data).to match a_hash_including( + 'runner1' => a_graphql_entity_for(active_project_runner, job_count: 2), + 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0) + ) + end + + context 'when JOB_COUNT_LIMIT is in effect' do + before do + stub_const('Types::Ci::RunnerType::JOB_COUNT_LIMIT', 0) + end + + it 'retrieves correct capped jobCount values' do + post_graphql(query, current_user: user) + + expect(graphql_data).to match a_hash_including( + 'runner1' => a_graphql_entity_for(active_project_runner, job_count: 1), + 'runner2' => a_graphql_entity_for(inactive_instance_runner, job_count: 0) ) end end @@ -243,18 +293,8 @@ RSpec.describe 'Query.runner(id)' do post_graphql(query, current_user: user) expect(graphql_data).to match a_hash_including( - 'runner1' => { - 'id' => runner1.to_global_id.to_s, - 'ownerProject' => { - 'id' => project2.to_global_id.to_s - } - }, - 'runner2' => { - 'id' => runner2.to_global_id.to_s, - 'ownerProject' => { - 'id' => project1.to_global_id.to_s - } - } + 'runner1' => a_graphql_entity_for(runner1, owner_project: a_graphql_entity_for(project2)), + 'runner2' => a_graphql_entity_for(runner2, owner_project: a_graphql_entity_for(project1)) ) end end @@ -284,8 +324,8 @@ RSpec.describe 'Query.runner(id)' do it 'retrieves groups field with expected value' do post_graphql(query, current_user: user) - runner_data = graphql_data_at(:runner, :groups) - expect(runner_data).to eq 'nodes' => [{ 'id' => group.to_global_id.to_s }] + runner_data = graphql_data_at(:runner, :groups, :nodes) + expect(runner_data).to contain_exactly(a_graphql_entity_for(group)) end end @@ -409,13 +449,13 @@ RSpec.describe 'Query.runner(id)' do 'jobCount' => 1, 'jobs' => a_hash_including( "count" => 1, - "nodes" => [{ "id" => job.to_global_id.to_s, "status" => job.status.upcase }] + "nodes" => [a_graphql_entity_for(job, status: job.status.upcase)] ), 'projectCount' => 2, 'projects' => { 'nodes' => [ - { 'id' => project1.to_global_id.to_s }, - { 'id' => project2.to_global_id.to_s } + a_graphql_entity_for(project1), + a_graphql_entity_for(project2) ] }) expect(runner2_data).to match a_hash_including( @@ -486,15 +526,24 @@ RSpec.describe 'Query.runner(id)' do groups { nodes { id + path + fullPath + webUrl } } projects { nodes { id + path + fullPath + webUrl } } ownerProject { id + path + fullPath + webUrl } } SINGLE @@ -503,8 +552,8 @@ RSpec.describe 'Query.runner(id)' do let(:active_project_runner2) { create(:ci_runner, :project) } let(:active_group_runner2) { create(:ci_runner, :group) } - # Currently excluding known N+1 issues, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759 - let(:excluded_fields) { %w[jobCount groups projects ownerProject] } + # Exclude fields that are already hardcoded above + let(:excluded_fields) { %w[jobs groups projects ownerProject] } let(:single_query) do <<~QUERY @@ -542,27 +591,98 @@ RSpec.describe 'Query.runner(id)' do expect(graphql_data.count).to eq 6 expect(graphql_data).to match( a_hash_including( - 'instance_runner1' => a_hash_including('id' => active_instance_runner.to_global_id.to_s), - 'instance_runner2' => a_hash_including('id' => inactive_instance_runner.to_global_id.to_s), - 'group_runner1' => a_hash_including( - 'id' => active_group_runner.to_global_id.to_s, - 'groups' => { 'nodes' => [a_hash_including('id' => group.to_global_id.to_s)] } + 'instance_runner1' => a_graphql_entity_for(active_instance_runner), + 'instance_runner2' => a_graphql_entity_for(inactive_instance_runner), + 'group_runner1' => a_graphql_entity_for( + active_group_runner, + groups: { 'nodes' => contain_exactly(a_graphql_entity_for(group)) } ), - 'group_runner2' => a_hash_including( - 'id' => active_group_runner2.to_global_id.to_s, - 'groups' => { 'nodes' => [a_hash_including('id' => active_group_runner2.groups[0].to_global_id.to_s)] } + 'group_runner2' => a_graphql_entity_for( + active_group_runner2, + groups: { 'nodes' => active_group_runner2.groups.map { |g| a_graphql_entity_for(g) } } ), - 'project_runner1' => a_hash_including( - 'id' => active_project_runner.to_global_id.to_s, - 'projects' => { 'nodes' => [a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)] }, - 'ownerProject' => a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s) + 'project_runner1' => a_graphql_entity_for( + active_project_runner, + projects: { 'nodes' => active_project_runner.projects.map { |p| a_graphql_entity_for(p) } }, + owner_project: a_graphql_entity_for(active_project_runner.projects[0]) ), - 'project_runner2' => a_hash_including( - 'id' => active_project_runner2.to_global_id.to_s, - 'projects' => { - 'nodes' => [a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)] - }, - 'ownerProject' => a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s) + 'project_runner2' => a_graphql_entity_for( + active_project_runner2, + projects: { 'nodes' => active_project_runner2.projects.map { |p| a_graphql_entity_for(p) } }, + owner_project: a_graphql_entity_for(active_project_runner2.projects[0]) + ) + )) + end + end + + describe 'Query limits with jobs' do + let!(:group1) { create(:group) } + let!(:group2) { create(:group) } + let!(:project1) { create(:project, :repository, group: group1) } + let!(:project2) { create(:project, :repository, group: group1) } + let!(:project3) { create(:project, :repository, group: group2) } + + let!(:merge_request1) { create(:merge_request, source_project: project1) } + let!(:merge_request2) { create(:merge_request, source_project: project3) } + + let(:project_runner2) { create(:ci_runner, :project, projects: [project1, project2]) } + let!(:build1) { create(:ci_build, :success, name: 'Build One', runner: project_runner2, pipeline: pipeline1) } + let!(:pipeline1) do + create(:ci_pipeline, project: project1, source: :merge_request_event, merge_request: merge_request1, ref: 'main', + target_sha: 'xxx') + end + + let(:query) do + <<~QUERY + { + runner(id: "#{project_runner2.to_global_id}") { + id + jobs { + nodes { + id + detailedStatus { + id + detailsPath + group + icon + text + } + shortSha + commitPath + finishedAt + duration + queuedDuration + tags + } + } + } + } + QUERY + end + + it 'does not execute more queries per job', :aggregate_failures do + # warm-up license cache and so on: + personal_access_token = create(:personal_access_token, user: user) + args = { current_user: user, token: { personal_access_token: personal_access_token } } + post_graphql(query, **args) + + control = ActiveRecord::QueryRecorder.new(query_recorder_debug: true) { post_graphql(query, **args) } + + # Add a new build to project_runner2 + project_runner2.runner_projects << build(:ci_runner_project, runner: project_runner2, project: project3) + pipeline2 = create(:ci_pipeline, project: project3, source: :merge_request_event, merge_request: merge_request2, + ref: 'main', target_sha: 'xxx') + build2 = create(:ci_build, :success, name: 'Build Two', runner: project_runner2, pipeline: pipeline2) + + args[:current_user] = create(:user, :admin) # do not reuse same user + expect { post_graphql(query, **args) }.not_to exceed_all_query_limit(control) + + expect(graphql_data.count).to eq 1 + expect(graphql_data).to match( + a_hash_including( + 'runner' => a_graphql_entity_for( + project_runner2, + jobs: { 'nodes' => containing_exactly(a_graphql_entity_for(build1), a_graphql_entity_for(build2)) } ) )) end diff --git a/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb index 767e958ea82..e84a1ca4cc4 100644 --- a/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb +++ b/spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'RunnerWebUrlEdge' do +RSpec.describe 'RunnerWebUrlEdge', feature_category: :runner_fleet do include GraphqlHelpers describe 'inside a Query.group' do diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb index 3054b866812..75d8609dc38 100644 --- a/spec/requests/api/graphql/ci/runners_spec.rb +++ b/spec/requests/api/graphql/ci/runners_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Query.runners' do +RSpec.describe 'Query.runners', feature_category: :runner_fleet do include GraphqlHelpers let_it_be(:current_user) { create_default(:user, :admin) } @@ -48,7 +48,7 @@ RSpec.describe 'Query.runners' do it_behaves_like 'a working graphql query' it 'returns expected runner' do - expect(runners_graphql_data['nodes'].map { |n| n['id'] }).to contain_exactly(expected_runner.to_global_id.to_s) + expect(runners_graphql_data['nodes']).to contain_exactly(a_graphql_entity_for(expected_runner)) end end diff --git a/spec/requests/api/graphql/ci/stages_spec.rb b/spec/requests/api/graphql/ci/stages_spec.rb index 1edd6e58486..f4e1a69d455 100644 --- a/spec/requests/api/graphql/ci/stages_spec.rb +++ b/spec/requests/api/graphql/ci/stages_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Query.project.pipeline.stages' do +RSpec.describe 'Query.project.pipeline.stages', feature_category: :continuous_integration do include GraphqlHelpers subject(:post_query) { post_graphql(query, current_user: user) } diff --git a/spec/requests/api/graphql/ci/template_spec.rb b/spec/requests/api/graphql/ci/template_spec.rb index 1bbef7d7f30..aaec219f734 100644 --- a/spec/requests/api/graphql/ci/template_spec.rb +++ b/spec/requests/api/graphql/ci/template_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Querying CI template' do +RSpec.describe 'Querying CI template', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :public) } 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 14c55e61a65..88f63fd59d7 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 @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'container repository details' do +RSpec.describe 'container repository details', feature_category: :container_registry do include_context 'container registry tags' include_context 'container registry client stubs' diff --git a/spec/requests/api/graphql/crm/contacts_spec.rb b/spec/requests/api/graphql/crm/contacts_spec.rb index a676e92dc3b..3ae19de63ed 100644 --- a/spec/requests/api/graphql/crm/contacts_spec.rb +++ b/spec/requests/api/graphql/crm/contacts_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting CRM contacts' do +RSpec.describe 'getting CRM contacts', feature_category: :service_desk do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/current_user/groups_query_spec.rb b/spec/requests/api/graphql/current_user/groups_query_spec.rb index 6e36beb2afc..151d07ff0a7 100644 --- a/spec/requests/api/graphql/current_user/groups_query_spec.rb +++ b/spec/requests/api/graphql/current_user/groups_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query current user groups' do +RSpec.describe 'Query current user groups', feature_category: :subgroups do include GraphqlHelpers let_it_be(:user) { create(:user) } 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 5a45f0db518..f7e23aeb241 100644 --- a/spec/requests/api/graphql/current_user/todos_query_spec.rb +++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query current user todos' do +RSpec.describe 'Query current user todos', feature_category: :source_code_management do include GraphqlHelpers include DesignManagementTestHelpers @@ -19,7 +19,7 @@ RSpec.describe 'Query current user todos' do let(:fields) do <<~QUERY nodes { - #{all_graphql_fields_for('todos'.classify)} + #{all_graphql_fields_for('todos'.classify, max_depth: 2)} } QUERY end diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb index 086a57094ca..53d2580caee 100644 --- a/spec/requests/api/graphql/current_user_query_spec.rb +++ b/spec/requests/api/graphql/current_user_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project information' do +RSpec.describe 'getting project information', feature_category: :authentication_and_authorization do include GraphqlHelpers let(:fields) do diff --git a/spec/requests/api/graphql/current_user_todos_spec.rb b/spec/requests/api/graphql/current_user_todos_spec.rb index da1c893ec2b..eaed51982e1 100644 --- a/spec/requests/api/graphql/current_user_todos_spec.rb +++ b/spec/requests/api/graphql/current_user_todos_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' -RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do +RSpec.describe 'A Todoable that implements the CurrentUserTodos interface', +feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/custom_emoji_query_spec.rb b/spec/requests/api/graphql/custom_emoji_query_spec.rb index 5dd5ad117b0..7b804623e01 100644 --- a/spec/requests/api/graphql/custom_emoji_query_spec.rb +++ b/spec/requests/api/graphql/custom_emoji_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting custom emoji within namespace' do +RSpec.describe 'getting custom emoji within namespace', feature_category: :not_owned do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_spec.rb index 6da00057449..0022a38d2d3 100644 --- a/spec/requests/api/graphql/environments/deployments_query_spec.rb +++ b/spec/requests/api/graphql/environments/deployments_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Environments Deployments query' do +RSpec.describe 'Environments Deployments query', feature_category: :continuous_delivery do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } @@ -437,6 +437,43 @@ RSpec.describe 'Environments Deployments query' do end end + context 'when requesting user permissions' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{environment.name}") { + deployments { + nodes { + iid + userPermissions { + updateDeployment + destroyDeployment + } + } + } + } + } + } + ) + end + + it_behaves_like 'avoids N+1 database queries' + + it 'returns user permissions of the deployments', :aggregate_failures do + deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes') + + deployments.each do |deployment| + deployment_in_record = project.deployments.find_by_iid(deployment['iid']) + + expect(deployment['userPermissions']['updateDeployment']) + .to eq(Ability.allowed?(user, :update_deployment, deployment_in_record)) + expect(deployment['userPermissions']['destroyDeployment']) + .to eq(Ability.allowed?(user, :destroy_deployment, deployment_in_record)) + end + end + end + describe 'sorting and pagination' do let(:data_path) { [:project, :environment, :deployments] } let(:current_user) { user } diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb index c1beadb6c45..7937091ea7c 100644 --- a/spec/requests/api/graphql/gitlab_schema_spec.rb +++ b/spec/requests/api/graphql/gitlab_schema_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'GitlabSchema configurations' do +RSpec.describe 'GitlabSchema configurations', feature_category: :not_owned do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/group/container_repositories_spec.rb b/spec/requests/api/graphql/group/container_repositories_spec.rb index 8ec321c8d7c..51d12261247 100644 --- a/spec/requests/api/graphql/group/container_repositories_spec.rb +++ b/spec/requests/api/graphql/group/container_repositories_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting container repositories in a group' do +RSpec.describe 'getting container repositories in a group', feature_category: :source_code_management do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb index daa1483e956..2c4770a31a7 100644 --- a/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb +++ b/spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting dependency proxy blobs in a group' do +RSpec.describe 'getting dependency proxy blobs in a group', feature_category: :dependency_proxy do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb index cc706c3051f..aca8527ba0a 100644 --- a/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb +++ b/spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting dependency proxy settings for a group' do +RSpec.describe 'getting dependency proxy settings for a group', feature_category: :dependency_proxy do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb index 3b2b04b1322..edff4dc1dae 100644 --- a/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb +++ b/spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting dependency proxy image ttl policy for a group' do +RSpec.describe 'getting dependency proxy image ttl policy for a group', feature_category: :dependency_proxy do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb b/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb index 37ef7089c2f..d2d686104ad 100644 --- a/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb +++ b/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting dependency proxy manifests in a group' do +RSpec.describe 'getting dependency proxy manifests in a group', feature_category: :dependency_proxy do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb index 5f8becc0726..26d1fb48408 100644 --- a/spec/requests/api/graphql/group/group_members_spec.rb +++ b/spec/requests/api/graphql/group/group_members_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting group members information' do +RSpec.describe 'getting group members information', feature_category: :subgroups do include GraphqlHelpers let_it_be(:parent_group) { create(:group, :public) } diff --git a/spec/requests/api/graphql/group/issues_spec.rb b/spec/requests/api/graphql/group/issues_spec.rb index 26338f46611..95aeed32558 100644 --- a/spec/requests/api/graphql/group/issues_spec.rb +++ b/spec/requests/api/graphql/group/issues_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting an issue list for a group' do +RSpec.describe 'getting an issue list for a group', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/group/labels_query_spec.rb b/spec/requests/api/graphql/group/labels_query_spec.rb index 31556ffca30..28886f8d80b 100644 --- a/spec/requests/api/graphql/group/labels_query_spec.rb +++ b/spec/requests/api/graphql/group/labels_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting group label information' do +RSpec.describe 'getting group label information', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group, :public) } diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb index 434b0d16569..6976685ecc0 100644 --- a/spec/requests/api/graphql/group/merge_requests_spec.rb +++ b/spec/requests/api/graphql/group/merge_requests_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' # Based on ee/spec/requests/api/epics_spec.rb # Should follow closely in order to ensure all situations are covered -RSpec.describe 'Query.group.mergeRequests' do +RSpec.describe 'Query.group.mergeRequests', feature_category: :code_review do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb index 7c51409f907..28cd68493c0 100644 --- a/spec/requests/api/graphql/group/milestones_spec.rb +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Milestones through GroupQuery' do +RSpec.describe 'Milestones through GroupQuery', feature_category: :team_planning do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/group/packages_spec.rb b/spec/requests/api/graphql/group/packages_spec.rb index cf8736db5af..0b4057c87f8 100644 --- a/spec/requests/api/graphql/group/packages_spec.rb +++ b/spec/requests/api/graphql/group/packages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a package list for a group' do +RSpec.describe 'getting a package list for a group', feature_category: :package_registry do include GraphqlHelpers let_it_be(:resource) { create(:group, :private) } diff --git a/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb b/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb index 4914beec870..2dfbc95bac9 100644 --- a/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb +++ b/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting group recent issue boards' do +RSpec.describe 'getting group recent issue boards', feature_category: :team_planning do include GraphqlHelpers it_behaves_like 'querying a GraphQL type recent boards' do diff --git a/spec/requests/api/graphql/group/timelogs_spec.rb b/spec/requests/api/graphql/group/timelogs_spec.rb index 05b6ee3ff89..b67b39edff2 100644 --- a/spec/requests/api/graphql/group/timelogs_spec.rb +++ b/spec/requests/api/graphql/group/timelogs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Timelogs through GroupQuery' do +RSpec.describe 'Timelogs through GroupQuery', feature_category: :team_planning do include GraphqlHelpers describe 'Get list of timelogs from a group issues' do diff --git a/spec/requests/api/graphql/group/work_item_types_spec.rb b/spec/requests/api/graphql/group/work_item_types_spec.rb index 35090e2a89f..791c0fb9524 100644 --- a/spec/requests/api/graphql/group/work_item_types_spec.rb +++ b/spec/requests/api/graphql/group/work_item_types_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a list of work item types for a group' do +RSpec.describe 'getting a list of work item types for a group', feature_category: :team_planning do include GraphqlHelpers let_it_be(:developer) { create(:user) } diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb index 8ee5c3c5d73..bc288c0a98b 100644 --- a/spec/requests/api/graphql/group_query_spec.rb +++ b/spec/requests/api/graphql/group_query_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' # Based on spec/requests/api/groups_spec.rb # Should follow closely in order to ensure all situations are covered -RSpec.describe 'getting group information' do +RSpec.describe 'getting group information', feature_category: :subgroups do include GraphqlHelpers include UploadHelpers diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb index 6e2d736f244..101de692aa5 100644 --- a/spec/requests/api/graphql/issue/issue_spec.rb +++ b/spec/requests/api/graphql/issue/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.issue(id)' do +RSpec.describe 'Query.issue(id)', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/issue_status_counts_spec.rb b/spec/requests/api/graphql/issue_status_counts_spec.rb index 89ecbf44b10..72a1968cb27 100644 --- a/spec/requests/api/graphql/issue_status_counts_spec.rb +++ b/spec/requests/api/graphql/issue_status_counts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting Issue counts by status' do +RSpec.describe 'getting Issue counts by status', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/issues_spec.rb b/spec/requests/api/graphql/issues_spec.rb index 8838ad78f72..ba6f8ec2cab 100644 --- a/spec/requests/api/graphql/issues_spec.rb +++ b/spec/requests/api/graphql/issues_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting an issue list at root level' do +RSpec.describe 'getting an issue list at root level', feature_category: :team_planning do include GraphqlHelpers let_it_be(:developer) { create(:user) } @@ -13,34 +13,81 @@ RSpec.describe 'getting an issue list at root level' do let_it_be(:project_b) { create(:project, :repository, :private, group: group1) } let_it_be(:project_c) { create(:project, :repository, :public, group: group2) } let_it_be(:project_d) { create(:project, :repository, :private, group: group2) } - let_it_be(:early_milestone) { create(:milestone, project: project_d, due_date: 10.days.from_now) } - let_it_be(:late_milestone) { create(:milestone, project: project_c, due_date: 30.days.from_now) } + let_it_be(:milestone1) { create(:milestone, project: project_c, due_date: 10.days.from_now) } + let_it_be(:milestone2) { create(:milestone, project: project_d, due_date: 20.days.from_now) } + let_it_be(:milestone3) { create(:milestone, project: project_d, due_date: 30.days.from_now) } + let_it_be(:milestone4) { create(:milestone, project: project_a, due_date: 40.days.from_now) } let_it_be(:priority1) { create(:label, project: project_c, priority: 1) } let_it_be(:priority2) { create(:label, project: project_d, priority: 5) } let_it_be(:priority3) { create(:label, project: project_a, priority: 10) } + let_it_be(:priority4) { create(:label, project: project_d, priority: 15) } + + let_it_be(:issue_a) do + create( + :issue, + project: project_a, + labels: [priority3], + due_date: 1.day.ago, + milestone: milestone4, + relative_position: 1000 + ) + end + + let_it_be(:issue_b) do + create( + :issue, + :with_alert, + project: project_b, + discussion_locked: true, + due_date: 1.day.from_now, + relative_position: 3000 + ) + end - let_it_be(:issue_a) { create(:issue, project: project_a, labels: [priority3]) } - let_it_be(:issue_b) { create(:issue, :with_alert, project: project_b, discussion_locked: true) } let_it_be(:issue_c) do create( :issue, + :confidential, project: project_c, title: 'title matching issue plus', labels: [priority1], - milestone: late_milestone + milestone: milestone1, + due_date: 3.days.from_now, + relative_position: nil ) end - let_it_be(:issue_d) { create(:issue, :with_alert, project: project_d, discussion_locked: true, labels: [priority2]) } - let_it_be(:issue_e) { create(:issue, project: project_d, milestone: early_milestone) } + let_it_be(:issue_d) do + create( + :issue, + :with_alert, + project: project_d, + discussion_locked: true, + labels: [priority2], + milestone: milestone3, + relative_position: 5000 + ) + end - let(:issue_filter_params) { {} } + let_it_be(:issue_e) do + create( + :issue, + :confidential, + project: project_d, + milestone: milestone2, + due_date: 3.days.ago, + relative_position: nil, + labels: [priority2, priority4] + ) + end + let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] } + + let(:issue_filter_params) { {} } + let(:current_user) { developer } let(:fields) do <<~QUERY - nodes { - #{all_graphql_fields_for('issues'.classify)} - } + nodes { id } QUERY end @@ -60,13 +107,16 @@ RSpec.describe 'getting an issue list at root level' do end end + # All new specs should be added to the shared example if the change also + # affects the `issues` query at the root level of the API. + # Shared example also used in spec/requests/api/graphql/project/issues_spec.rb it_behaves_like 'graphql issue list request spec' do - subject(:post_query) { post_graphql(query, current_user: current_user) } + let_it_be(:external_user) { create(:user) } + + let(:public_projects) { [project_a, project_c] } - let(:current_user) { developer } let(:another_user) { reporter } - let(:issues_data) { graphql_data['issues']['nodes'] } - let(:issue_ids) { graphql_dig_at(issues_data, :id) } + let(:issue_nodes_path) { %w[issues nodes] } # filters let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] } @@ -77,12 +127,25 @@ RSpec.describe 'getting an issue list at root level' do let(:unlocked_discussion_issues) { [issue_a, issue_c, issue_e] } let(:search_title_term) { 'matching issue' } let(:title_search_issue) { issue_c } + let(:confidential_issues) { [issue_c, issue_e] } + let(:non_confidential_issues) { [issue_a, issue_b, issue_d] } + let(:public_non_confidential_issues) { [issue_a] } # sorting let(:data_path) { [:issues] } - let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] } - let(:expected_priority_sorted_asc) { [issue_e, issue_c, issue_d, issue_a, issue_b] } - let(:expected_priority_sorted_desc) { [issue_c, issue_e, issue_a, issue_d, issue_b] } + let(:expected_priority_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] } + let(:expected_priority_sorted_desc) { [issue_a, issue_d, issue_e, issue_c, issue_b] } + let(:expected_due_date_sorted_desc) { [issue_c, issue_b, issue_a, issue_e, issue_d] } + let(:expected_due_date_sorted_asc) { [issue_e, issue_a, issue_b, issue_c, issue_d] } + let(:expected_relative_position_sorted_asc) { [issue_a, issue_b, issue_d, issue_c, issue_e] } + let(:expected_label_priority_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] } + let(:expected_label_priority_sorted_desc) { [issue_a, issue_e, issue_d, issue_c, issue_b] } + let(:expected_milestone_sorted_asc) { [issue_c, issue_e, issue_d, issue_a, issue_b] } + let(:expected_milestone_sorted_desc) { [issue_a, issue_d, issue_e, issue_c, issue_b] } + + # N+1 queries + let(:same_project_issue1) { issue_d } + let(:same_project_issue2) { issue_e } before_all do issue_a.assignee_ids = developer.id @@ -90,12 +153,6 @@ RSpec.describe 'getting an issue list at root level' do create(:award_emoji, :upvote, user: developer, awardable: issue_a) create(:award_emoji, :upvote, user: developer, awardable: issue_c) - - # severity sorting - create(:issuable_severity, issue: issue_a, severity: :unknown) - create(:issuable_severity, issue: issue_b, severity: :low) - create(:issuable_severity, issue: issue_d, severity: :critical) - create(:issuable_severity, issue: issue_e, severity: :high) end def pagination_query(params) @@ -107,6 +164,27 @@ RSpec.describe 'getting an issue list at root level' do end end + context 'when fetching issues from multiple projects' do + it 'avoids N+1 queries' do + post_query # warm-up + + control = ActiveRecord::QueryRecorder.new { post_query } + + new_private_project = create(:project, :private).tap { |project| project.add_developer(current_user) } + create(:issue, project: new_private_project) + + expect { post_query }.not_to exceed_query_limit(control) + end + end + + def execute_query + post_query + end + + def post_query(request_user = current_user) + post_graphql(query, current_user: request_user) + end + def query(params = issue_filter_params) graphql_query_for( :issues, diff --git a/spec/requests/api/graphql/jobs_query_spec.rb b/spec/requests/api/graphql/jobs_query_spec.rb index 5907566be7f..0aea8e4c253 100644 --- a/spec/requests/api/graphql/jobs_query_spec.rb +++ b/spec/requests/api/graphql/jobs_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting job information' do +RSpec.describe 'getting job information', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:job) { create(:ci_build, :success, name: 'job1') } diff --git a/spec/requests/api/graphql/merge_request/merge_request_spec.rb b/spec/requests/api/graphql/merge_request/merge_request_spec.rb index d89f381753e..213697bacc1 100644 --- a/spec/requests/api/graphql/merge_request/merge_request_spec.rb +++ b/spec/requests/api/graphql/merge_request/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.merge_request(id)' do +RSpec.describe 'Query.merge_request(id)', feature_category: :code_review do include GraphqlHelpers let_it_be(:project) { create(:project, :empty_repo) } diff --git a/spec/requests/api/graphql/metadata_query_spec.rb b/spec/requests/api/graphql/metadata_query_spec.rb index 435e1b5b596..7d1850b1b93 100644 --- a/spec/requests/api/graphql/metadata_query_spec.rb +++ b/spec/requests/api/graphql/metadata_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project information' do +RSpec.describe 'getting project information', feature_category: :projects do include GraphqlHelpers let(:query) { graphql_query_for('metadata', {}, all_graphql_fields_for('Metadata')) } diff --git a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb index 72ec2b8e070..4dd47142c40 100644 --- a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb +++ b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting Metrics Dashboard Annotations' do +RSpec.describe 'Getting Metrics Dashboard Annotations', feature_category: :metrics do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb index 1b84acff0e2..8db0844c6d7 100644 --- a/spec/requests/api/graphql/metrics/dashboard_query_spec.rb +++ b/spec/requests/api/graphql/metrics/dashboard_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting Metrics Dashboard' do +RSpec.describe 'Getting Metrics Dashboard', feature_category: :metrics do include GraphqlHelpers let_it_be(:current_user) { create(:user) } @@ -26,156 +26,73 @@ RSpec.describe 'Getting Metrics Dashboard' do ) end - context 'with metrics_dashboard_exhaustive_validations feature flag off' do + context 'for anonymous user' do before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: false) + post_graphql(query, current_user: current_user) end - context 'for anonymous user' do - before do - post_graphql(query, current_user: current_user) - end - - context 'requested dashboard is available' do - let(:path) { 'config/prometheus/common_metrics.yml' } - - it_behaves_like 'a working graphql query' - - it 'returns nil' do - dashboard = graphql_data.dig('project', 'environments', 'nodes') - - expect(dashboard).to be_nil - end - end - end - - context 'for user with developer access' do - before do - project.add_developer(current_user) - post_graphql(query, current_user: current_user) - end - - context 'requested dashboard is available' do - let(:path) { 'config/prometheus/common_metrics.yml' } - - it_behaves_like 'a working graphql query' - - it 'returns metrics dashboard' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil) - end - - context 'invalid dashboard' do - let(:path) { '.gitlab/dashboards/metrics.yml' } - let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) } - - it 'returns metrics dashboard' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"]) - end - end - - context 'empty dashboard' do - let(:path) { '.gitlab/dashboards/metrics.yml' } - let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) } - - it 'returns metrics dashboard' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') + context 'requested dashboard is available' do + let(:path) { 'config/prometheus/common_metrics.yml' } - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"]) - end - end - end - - context 'requested dashboard can not be found' do - let(:path) { 'config/prometheus/i_am_not_here.yml' } + it_behaves_like 'a working graphql query' - it_behaves_like 'a working graphql query' + it 'returns nil' do + dashboard = graphql_data.dig('project', 'environments', 'nodes') - it 'returns nil' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - - expect(dashboard).to be_nil - end + expect(dashboard).to be_nil end end end - context 'with metrics_dashboard_exhaustive_validations feature flag on' do + context 'for user with developer access' do before do - stub_feature_flags(metrics_dashboard_exhaustive_validations: true) + project.add_developer(current_user) + post_graphql(query, current_user: current_user) end - context 'for anonymous user' do - before do - post_graphql(query, current_user: current_user) - end - - context 'requested dashboard is available' do - let(:path) { 'config/prometheus/common_metrics.yml' } - - it_behaves_like 'a working graphql query' + context 'requested dashboard is available' do + let(:path) { 'config/prometheus/common_metrics.yml' } - it 'returns nil' do - dashboard = graphql_data.dig('project', 'environments', 'nodes') + it_behaves_like 'a working graphql query' - expect(dashboard).to be_nil - end - end - end + it 'returns metrics dashboard' do + dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - context 'for user with developer access' do - before do - project.add_developer(current_user) - post_graphql(query, current_user: current_user) + expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil) end - context 'requested dashboard is available' do - let(:path) { 'config/prometheus/common_metrics.yml' } - - it_behaves_like 'a working graphql query' + context 'invalid dashboard' do + let(:path) { '.gitlab/dashboards/metrics.yml' } + let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) } it 'returns metrics dashboard' do dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => nil) - end - - context 'invalid dashboard' do - let(:path) { '.gitlab/dashboards/metrics.yml' } - let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "---\ndashboard: 'test'" }) } - - it 'returns metrics dashboard' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: panel_groups"]) - end + expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["panel_groups: should be an array of panel_groups objects"]) end + end - context 'empty dashboard' do - let(:path) { '.gitlab/dashboards/metrics.yml' } - let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) } + context 'empty dashboard' do + let(:path) { '.gitlab/dashboards/metrics.yml' } + let(:project) { create(:project, :repository, :custom_repo, namespace: current_user.namespace, files: { path => "" }) } - it 'returns metrics dashboard' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') + it 'returns metrics dashboard' do + dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["root is missing required keys: dashboard, panel_groups"]) - end + expect(dashboard).to eql("path" => path, "schemaValidationWarnings" => ["dashboard: can't be blank", "panel_groups: should be an array of panel_groups objects"]) end end + end - context 'requested dashboard can not be found' do - let(:path) { 'config/prometheus/i_am_not_here.yml' } + context 'requested dashboard can not be found' do + let(:path) { 'config/prometheus/i_am_not_here.yml' } - it_behaves_like 'a working graphql query' + it_behaves_like 'a working graphql query' - it 'returns nil' do - dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') + it 'returns nil' do + dashboard = graphql_data.dig('project', 'environments', 'nodes', 0, 'metricsDashboard') - expect(dashboard).to be_nil - end + expect(dashboard).to be_nil end end end diff --git a/spec/requests/api/graphql/milestone_spec.rb b/spec/requests/api/graphql/milestone_spec.rb index 78e7ec39ee3..2cea9fd0408 100644 --- a/spec/requests/api/graphql/milestone_spec.rb +++ b/spec/requests/api/graphql/milestone_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Querying a Milestone' do +RSpec.describe 'Querying a Milestone', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group, :public) } diff --git a/spec/requests/api/graphql/multiplexed_queries_spec.rb b/spec/requests/api/graphql/multiplexed_queries_spec.rb index f79bac6ae3b..4d615d3eaa4 100644 --- a/spec/requests/api/graphql/multiplexed_queries_spec.rb +++ b/spec/requests/api/graphql/multiplexed_queries_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Multiplexed queries' do +RSpec.describe 'Multiplexed queries', feature_category: :not_owned do include GraphqlHelpers it 'returns responses for multiple queries' do diff --git a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb index f992e46879f..64ea6d32f5f 100644 --- a/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb +++ b/spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues do +RSpec.describe 'Deleting Sidekiq jobs', :clean_gitlab_redis_queues, feature_category: :not_owned do include GraphqlHelpers let_it_be(:admin) { create(:admin) } diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb index f637ca98353..fbe6d95dfff 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create an alert issue from an alert' do +RSpec.describe 'Create an alert issue from an alert', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb index fcef7b4e3ec..935856814c4 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting assignees of an alert' do +RSpec.describe 'Setting assignees of an alert', feature_category: :incident_management do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb index 48307964345..570324a3126 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating a todo for the alert' do +RSpec.describe 'Creating a todo for the alert', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb index 802d8d6c5a1..6537747850c 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting the status of an alert' do +RSpec.describe 'Setting the status of an alert', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb index ff93da2153f..187c88363c6 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating a new HTTP Integration' do +RSpec.describe 'Creating a new HTTP Integration', feature_category: :integrations do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb index 1ecb5c76b57..1c77c71daba 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Removing an HTTP Integration' do +RSpec.describe 'Removing an HTTP Integration', feature_category: :integrations do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb index badd9412589..427277dd540 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Resetting a token on an existing HTTP Integration' do +RSpec.describe 'Resetting a token on an existing HTTP Integration', feature_category: :integrations do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb index 18cbb7d8b00..a9d189d564d 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating an existing HTTP Integration' do +RSpec.describe 'Updating an existing HTTP Integration', feature_category: :integrations do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb index 4c359d9b357..3dee7f50af3 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating a new Prometheus Integration' do +RSpec.describe 'Creating a new Prometheus Integration', feature_category: :incident_management do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb index 31053c50cac..15127843b95 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Resetting a token on an existing Prometheus Integration' do +RSpec.describe 'Resetting a token on an existing Prometheus Integration', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb index ad26ec118d7..63e95f4513b 100644 --- a/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating an existing Prometheus Integration' do +RSpec.describe 'Updating an existing Prometheus Integration', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb index 3879e58cecf..fdbff0f93cd 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Adding an AwardEmoji' do +RSpec.describe 'Adding an AwardEmoji', feature_category: :not_owned do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb index e81621209fb..e200bfc2d18 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Removing an AwardEmoji' do +RSpec.describe 'Removing an AwardEmoji', feature_category: :not_owned do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb index b151da72b55..6dba2b58357 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Toggling an AwardEmoji' do +RSpec.describe 'Toggling an AwardEmoji', feature_category: :not_owned do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/boards/create_spec.rb b/spec/requests/api/graphql/mutations/boards/create_spec.rb index ca848c0c92f..10eb12f277f 100644 --- a/spec/requests/api/graphql/mutations/boards/create_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Boards::Create do +RSpec.describe Mutations::Boards::Create, feature_category: :team_planning do let_it_be(:parent) { create(:project) } let_it_be(:current_user, reload: true) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/boards/destroy_spec.rb b/spec/requests/api/graphql/mutations/boards/destroy_spec.rb index 7620da3e7e0..44924159137 100644 --- a/spec/requests/api/graphql/mutations/boards/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Boards::Destroy do +RSpec.describe Mutations::Boards::Destroy, feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user, reload: true) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb b/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb index 06093e9f7c2..df64caa1cfb 100644 --- a/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Reposition and move issue within board lists' do +RSpec.describe 'Reposition and move issue within board lists', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group, :private) } diff --git a/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb index fec9a8c6307..f5381be2741 100644 --- a/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a label or backlog board list' do +RSpec.describe 'Create a label or backlog board list', feature_category: :team_planning do let_it_be(:group) { create(:group, :private) } let_it_be(:board) { create(:board, group: group) } diff --git a/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb index 83309ead352..b37b1a7b935 100644 --- a/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Boards::Lists::Destroy do +RSpec.describe Mutations::Boards::Lists::Destroy, feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user, reload: true) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb b/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb index c7885879a9d..6a7edcd7349 100644 --- a/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb +++ b/spec/requests/api/graphql/mutations/boards/lists/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Update of an existing board list' do +RSpec.describe 'Update of an existing board list', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/branches/create_spec.rb b/spec/requests/api/graphql/mutations/branches/create_spec.rb index 9ee2f41e8fc..32512e2ee1b 100644 --- a/spec/requests/api/graphql/mutations/branches/create_spec.rb +++ b/spec/requests/api/graphql/mutations/branches/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new branch' do +RSpec.describe 'Creation of a new branch', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:group) { create(:group, :public) } diff --git a/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb index bdad80995ea..6cdf8788957 100644 --- a/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'JobArtifactsDestroy' do +RSpec.describe 'JobArtifactsDestroy', feature_category: :build_artifacts do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb index 5855eb6bb51..88dfec41d36 100644 --- a/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'JobArtifactsDestroy' do +RSpec.describe 'JobArtifactsDestroy', feature_category: :build_artifacts do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb index a5ec9ea343d..ac3592130b8 100644 --- a/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ArtifactDestroy' do +RSpec.describe 'ArtifactDestroy', feature_category: :build_artifacts do include GraphqlHelpers let(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb index ee0f0a9bccb..468a9e57f56 100644 --- a/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "JobCancel" do +RSpec.describe "JobCancel", feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb index 0874e225259..014a5e0f1c7 100644 --- a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'JobPlay' do +RSpec.describe 'JobPlay', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb b/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb index 8cf559a372a..e49ee6f3163 100644 --- a/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_retry_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'JobRetry' do +RSpec.describe 'JobRetry', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb index b2f84ab2869..490716ddbe2 100644 --- a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'CiJobTokenScopeAddProject' do +RSpec.describe 'CiJobTokenScopeAddProject', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } @@ -60,7 +60,7 @@ RSpec.describe 'CiJobTokenScopeAddProject' do post_graphql_mutation(mutation, current_user: current_user) expect(response).to have_gitlab_http_status(:success) expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty - end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(false).to(true) + end.to change { Ci::JobToken::Scope.new(project).allows?(target_project) }.from(false).to(true) end context 'when invalid target project is provided' do diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb index 2b0adf89f40..607c6bd85c2 100644 --- a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'CiJobTokenScopeRemoveProject' do +RSpec.describe 'CiJobTokenScopeRemoveProject', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } @@ -66,7 +66,7 @@ RSpec.describe 'CiJobTokenScopeRemoveProject' do post_graphql_mutation(mutation, current_user: current_user) expect(response).to have_gitlab_http_status(:success) expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty - end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(true).to(false) + end.to change { Ci::JobToken::Scope.new(project).allows?(target_project) }.from(true).to(false) end context 'when invalid target project is provided' do diff --git a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb b/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb index 4ddc019a2b5..6868b0ea279 100644 --- a/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'JobUnschedule' do +RSpec.describe 'JobUnschedule', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb index 6ec1b7ce9b6..8c1359384ed 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'PipelineCancel' do +RSpec.describe 'PipelineCancel', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb index 7abd5ca8772..9ddfaf83d34 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'PipelineDestroy' do +RSpec.describe 'PipelineDestroy', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb index f6acf29c321..e7edc86bea0 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'PipelineRetry' do +RSpec.describe 'PipelineRetry', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb new file mode 100644 index 00000000000..4a45d255d99 --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_create_spec.rb @@ -0,0 +1,151 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'PipelineSchedulecreate' do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public, :repository) } + + let(:mutation) do + variables = { + project_path: project.full_path, + **pipeline_schedule_parameters + } + + graphql_mutation( + :pipeline_schedule_create, + variables, + <<-QL + pipelineSchedule { + id + description + cron + refForDisplay + active + cronTimezone + variables { + nodes { + key + value + } + } + owner { + id + } + } + errors + QL + ) + end + + let(:pipeline_schedule_parameters) do + { + description: 'created_desc', + cron: '0 1 * * *', + cronTimezone: 'UTC', + ref: 'patch-x', + active: true, + variables: [ + { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' } + ] + } + end + + let(:mutation_response) { graphql_mutation_response(:pipeline_schedule_create) } + + context 'when unauthorized' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: user) + + expect(graphql_errors).not_to be_empty + expect(graphql_errors[0]['message']) + .to eq( + "The resource that you are attempting to access does not exist " \ + "or you don't have permission to perform this action" + ) + end + end + + context 'when authorized' do + before do + project.add_developer(user) + end + + context 'when success' do + it do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + + expect(mutation_response['pipelineSchedule']['owner']['id']).to eq(user.to_global_id.to_s) + + %w[description cron cronTimezone active].each do |key| + expect(mutation_response['pipelineSchedule'][key]).to eq(pipeline_schedule_parameters[key.to_sym]) + end + + expect(mutation_response['pipelineSchedule']['refForDisplay']).to eq(pipeline_schedule_parameters[:ref]) + + expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['key']).to eq('AAA') + expect(mutation_response['pipelineSchedule']['variables']['nodes'][0]['value']).to eq('AAA123') + + expect(mutation_response['pipelineSchedule']['owner']['id']).to eq(user.to_global_id.to_s) + + expect(mutation_response['errors']).to eq([]) + end + end + + context 'when failure' do + context 'when params are invalid' do + let(:pipeline_schedule_parameters) do + { + description: 'some description', + cron: 'abc', + cronTimezone: 'cCc', + ref: 'asd', + active: true, + variables: [] + } + end + + it do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + + expect(mutation_response['errors']) + .to match_array( + ["Cron is invalid syntax", "Cron timezone is invalid syntax"] + ) + end + end + + context 'when variables have duplicate name' do + before do + pipeline_schedule_parameters.merge!( + { + variables: [ + { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' }, + { key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' } + ] + } + ) + end + + it 'returns error' do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + + expect(mutation_response['errors']) + .to match_array( + [ + "Variables have duplicate values (AAA)" + ] + ) + end + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb index b197d223463..b846ff0aec8 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'PipelineScheduleDelete' do +RSpec.describe 'PipelineScheduleDelete', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb new file mode 100644 index 00000000000..0e43fa024f3 --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_play_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'PipelineSchedulePlay', feature_category: :continuious_integration do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:pipeline_schedule) do + create( + :ci_pipeline_schedule, + :every_minute, + project: project, + owner: user + ) + end + + let(:mutation) do + graphql_mutation( + :pipeline_schedule_play, + { id: pipeline_schedule.to_global_id.to_s }, + <<-QL + pipelineSchedule { id, nextRunAt } + errors + QL + ) + end + + let(:mutation_response) { graphql_mutation_response(:pipeline_schedule_play) } + + context 'when unauthorized' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + expect(graphql_errors[0]['message']) + .to eq( + "The resource that you are attempting to access does not exist " \ + "or you don't have permission to perform this action" + ) + end + end + + context 'when authorized' do + before do + project.add_maintainer(user) + pipeline_schedule.update_columns(next_run_at: 2.hours.ago) + end + + context 'when mutation succeeds' do + it do + post_graphql_mutation(mutation, current_user: user) + + expect(mutation_response['pipelineSchedule']['id']).to include(pipeline_schedule.id.to_s) + new_next_run_at = DateTime.parse(mutation_response['pipelineSchedule']['nextRunAt']) + expect(new_next_run_at).not_to eq(pipeline_schedule.next_run_at) + expect(new_next_run_at).to eq(pipeline_schedule.reset.next_run_at) + expect(mutation_response['errors']).to eq([]) + end + end + + context 'when mutation fails' do + before do + allow(RunPipelineScheduleWorker).to receive(:perform_async).and_return(nil) + end + + it do + expect(RunPipelineScheduleWorker) + .to receive(:perform_async) + .with(pipeline_schedule.id, user.id) + + post_graphql_mutation(mutation, current_user: user) + + expect(mutation_response['pipelineSchedule']).to be_nil + expect(mutation_response['errors']).to match_array(['Unable to schedule a pipeline to run immediately.']) + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb index 8dfbf20d00b..2d1f1565a73 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_schedule_take_ownership_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'PipelineScheduleTakeOwnership' do +RSpec.describe 'PipelineScheduleTakeOwnership', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb index c808cf5ede9..7a6ee7c2ecc 100644 --- a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ProjectCiCdSettingsUpdate' do +RSpec.describe 'ProjectCiCdSettingsUpdate', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) do diff --git a/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb index 54e63df96a6..752242c3ab3 100644 --- a/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'RunnersRegistrationTokenReset' do +RSpec.describe 'RunnersRegistrationTokenReset', feature_category: :runner_fleet do include GraphqlHelpers let(:mutation) { graphql_mutation(:runners_registration_token_reset, input) } diff --git a/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb b/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb index aac8eb22771..f544cef8864 100644 --- a/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb +++ b/spec/requests/api/graphql/mutations/clusters/agent_tokens/agent_tokens/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a new cluster agent token' do +RSpec.describe 'Create a new cluster agent token', feature_category: :kubernetes_management do include GraphqlHelpers let_it_be(:cluster_agent) { create(:cluster_agent) } diff --git a/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb b/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb index c2ef2362d66..66e6c5cc629 100644 --- a/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb +++ b/spec/requests/api/graphql/mutations/clusters/agents/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a new cluster agent' do +RSpec.describe 'Create a new cluster agent', feature_category: :kubernetes_management do include GraphqlHelpers let(:project) { create(:project, :public, :repository) } diff --git a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb index 4891e64aab8..27a566dfb8c 100644 --- a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Delete a cluster agent' do +RSpec.describe 'Delete a cluster agent', feature_category: :kubernetes_management do include GraphqlHelpers let(:cluster_agent) { create(:cluster_agent) } diff --git a/spec/requests/api/graphql/mutations/commits/create_spec.rb b/spec/requests/api/graphql/mutations/commits/create_spec.rb index 619cba99c4e..e298d8284c6 100644 --- a/spec/requests/api/graphql/mutations/commits/create_spec.rb +++ b/spec/requests/api/graphql/mutations/commits/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new commit' do +RSpec.describe 'Creation of a new commit', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb index ca7c1b2ce5f..97e2a5d0bf7 100644 --- a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb +++ b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating the container expiration policy' do +RSpec.describe 'Updating the container expiration policy', feature_category: :container_registry do include GraphqlHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb index 5a27d39ecbc..8b76c19cda6 100644 --- a/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/container_repository/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying a container repository' do +RSpec.describe 'Destroying a container repository', feature_category: :container_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers @@ -80,25 +80,6 @@ RSpec.describe 'Destroying a container repository' do it_behaves_like params[:shared_examples_name] end - - context 'with container_registry_delete_repository_with_cron_worker disabled' do - before do - project.add_maintainer(user) - stub_feature_flags(container_registry_delete_repository_with_cron_worker: false) - end - - it 'enqueues a removal job' do - expect(::Packages::CreateEventService) - .to receive(:new).with(nil, user, event_name: :delete_repository, scope: :container).and_call_original - expect(DeleteContainerRepositoryWorker) - .to receive(:perform_async).with(user.id, container_repository.id) - - expect { subject }.to change { ::Packages::Event.count }.by(1) - - expect(container_repository_mutation_response).to match_schema('graphql/container_repository') - expect(container_repository_mutation_response['status']).to eq('DELETE_SCHEDULED') - end - end end context 'with invalid id' do diff --git a/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb index ef00f45ef18..9e07a831076 100644 --- a/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb +++ b/spec/requests/api/graphql/mutations/container_repository/destroy_tags_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying a container repository tags' do +RSpec.describe 'Destroying a container repository tags', feature_category: :container_registry do include_context 'container repository delete tags service shared context' using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb index 66facdebe78..ea2ce8a13e2 100644 --- a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb +++ b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new Custom Emoji' do +RSpec.describe 'Creation of a new Custom Emoji', feature_category: :not_owned do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb index 7d25206e617..ad7a043909a 100644 --- a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Deletion of custom emoji' do +RSpec.describe 'Deletion of custom emoji', feature_category: :not_owned do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb b/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb index 9eb13e534ac..5d5696d3f66 100644 --- a/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb +++ b/spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating the dependency proxy group settings' do +RSpec.describe 'Updating the dependency proxy group settings', feature_category: :dependency_proxy do include GraphqlHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb b/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb index 31ba7ecdf0e..66ee17f356c 100644 --- a/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb +++ b/spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating the dependency proxy image ttl policy' do +RSpec.describe 'Updating the dependency proxy image ttl policy', feature_category: :dependency_proxy do include GraphqlHelpers using RSpec::Parameterized::TableSyntax 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 e2ab08b301b..7ea32ae6d19 100644 --- a/spec/requests/api/graphql/mutations/design_management/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/design_management/delete_spec.rb @@ -2,7 +2,7 @@ require "spec_helper" -RSpec.describe "deleting designs" do +RSpec.describe "deleting designs", feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/mutations/design_management/move_spec.rb b/spec/requests/api/graphql/mutations/design_management/move_spec.rb index dd121ec733e..27b5259c56b 100644 --- a/spec/requests/api/graphql/mutations/design_management/move_spec.rb +++ b/spec/requests/api/graphql/mutations/design_management/move_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "spec_helper" -RSpec.describe "moving designs" do +RSpec.describe "moving designs", feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/mutations/design_management/upload_spec.rb b/spec/requests/api/graphql/mutations/design_management/upload_spec.rb index d3e6c689a59..9b42b32c150 100644 --- a/spec/requests/api/graphql/mutations/design_management/upload_spec.rb +++ b/spec/requests/api/graphql/mutations/design_management/upload_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "spec_helper" -RSpec.describe "uploading designs" do +RSpec.describe "uploading designs", feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers include WorkhorseHelpers diff --git a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb index 632a934cd95..16d3bbb6518 100644 --- a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb +++ b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Toggling the resolve status of a discussion' do +RSpec.describe 'Toggling the resolve status of a discussion', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project, :public, :repository) } diff --git a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb index 3771ae0746e..0e9317a4879 100644 --- a/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb +++ b/spec/requests/api/graphql/mutations/environments/canary_ingress/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache do +RSpec.describe 'Update Environment Canary Ingress', :clean_gitlab_redis_cache, feature_category: :deployment_management do include GraphqlHelpers include KubernetesHelpers diff --git a/spec/requests/api/graphql/mutations/groups/update_spec.rb b/spec/requests/api/graphql/mutations/groups/update_spec.rb index b9dfb8e37ab..ea3d42a4463 100644 --- a/spec/requests/api/graphql/mutations/groups/update_spec.rb +++ b/spec/requests/api/graphql/mutations/groups/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'GroupUpdate' do +RSpec.describe 'GroupUpdate', feature_category: :subgroups do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb index fc3b666dd3d..49cee4f6801 100644 --- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating an incident timeline event' do +RSpec.describe 'Creating an incident timeline event', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb index 85208869ad9..6e1a7b36736 100644 --- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Removing an incident timeline event' do +RSpec.describe 'Removing an incident timeline event', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb index 62eeecb3fb7..ca9557b3183 100644 --- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Promote an incident timeline event from a comment' do +RSpec.describe 'Promote an incident timeline event from a comment', feature_category: :incident_management do include GraphqlHelpers include NotesHelper diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb index 542d51b990f..163c689e399 100644 --- a/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb @@ -2,24 +2,36 @@ require 'spec_helper' -RSpec.describe 'Updating an incident timeline event' do +RSpec.describe 'Updating an incident timeline event', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:incident) { create(:incident, project: project) } + let_it_be(:tag1) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 1') } + let_it_be(:tag2) { create(:incident_management_timeline_event_tag, project: project, name: 'Tag 2') } let_it_be_with_reload(:timeline_event) do create(:incident_management_timeline_event, incident: incident, project: project) end + # Pre-attach a tag to the event + let_it_be(:tag_link1) do + create(:incident_management_timeline_event_tag_link, + timeline_event: timeline_event, + timeline_event_tag: tag1 + ) + end + let(:occurred_at) { 1.minute.ago.iso8601 } let(:note) { 'Updated note' } + let(:tag_names) { [] } let(:variables) do { id: timeline_event.to_global_id.to_s, note: note, - occurred_at: occurred_at + occurred_at: occurred_at, + timeline_event_tag_names: tag_names } end @@ -33,6 +45,7 @@ RSpec.describe 'Updating an incident timeline event' do author { id username } updatedByUser { id username } incident { id title } + timelineEventTags { nodes { name } } note noteHtml occurredAt @@ -71,6 +84,9 @@ RSpec.describe 'Updating an incident timeline event' do 'id' => incident.to_global_id.to_s, 'title' => incident.title }, + 'timelineEventTags' => { + 'nodes' => [] + }, 'note' => note, 'noteHtml' => timeline_event.note_html, 'occurredAt' => occurred_at, @@ -85,4 +101,27 @@ RSpec.describe 'Updating an incident timeline event' do it_behaves_like 'timeline event mutation responds with validation error', error_message: 'Timeline text is too long (maximum is 280 characters)' end + + context 'when timeline event tag names are passed' do + context 'when tags exist' do + let(:tag_names) { [tag2.name] } + + it 'removes tag1 and adds tag2' do + post_graphql_mutation(mutation, current_user: user) + + timeline_event_response = mutation_response['timelineEvent'] + tag_names = timeline_event_response['timelineEventTags']['nodes'] + + expect(response).to have_gitlab_http_status(:success) + expect(tag_names).to contain_exactly({ "name" => tag2.name }) + end + end + + context 'when tags do not exist' do + let(:tag_names) { ['some other tag'] } + + it_behaves_like 'timeline event mutation responds with validation error', + error_message: "Following tags don't exist: [\"some other tag\"]" + end + end end diff --git a/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb b/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb index 7476499d9da..b37a5331421 100644 --- a/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event_tag/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating a timeline event tag' do +RSpec.describe 'Creating a timeline event tag', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/create_spec.rb b/spec/requests/api/graphql/mutations/issues/create_spec.rb index a489b7424e8..d2d2f0014d6 100644 --- a/spec/requests/api/graphql/mutations/issues/create_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create an issue' do +RSpec.describe 'Create an issue', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb b/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb new file mode 100644 index 00000000000..85e21952f47 --- /dev/null +++ b/spec/requests/api/graphql/mutations/issues/link_alerts_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Link alerts to an incident', feature_category: :incident_management do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:linked_alert) { create(:alert_management_alert, project: project) } + let_it_be(:alert1) { create(:alert_management_alert, project: project) } + let_it_be(:alert2) { create(:alert_management_alert, project: project) } + let_it_be(:incident) { create(:incident, project: project, alert_management_alerts: [linked_alert]) } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: incident.iid.to_s, + alert_references: [alert1.to_reference, alert2.details_url] + } + + graphql_mutation(:issue_link_alerts, variables, + <<-QL.strip_heredoc + clientMutationId + errors + issue { + iid + alertManagementAlerts { + nodes { + iid + } + } + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:issue_link_alerts) + end + + context 'when the user is not allowed to update the incident' do + it 'returns an error' do + error = Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_errors).to include(a_hash_including('message' => error)) + end + end + + context 'when the user is allowed to update the incident' do + before do + project.add_developer(user) + end + + it 'links alerts to the incident' do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expected_response = [linked_alert, alert1, alert2].map { |a| { 'iid' => a.iid.to_s } } + expect(mutation_response.dig('issue', 'alertManagementAlerts', 'nodes')).to match_array(expected_response) + end + end +end diff --git a/spec/requests/api/graphql/mutations/issues/move_spec.rb b/spec/requests/api/graphql/mutations/issues/move_spec.rb index 20ed16879f6..7d9579067b6 100644 --- a/spec/requests/api/graphql/mutations/issues/move_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/move_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Moving an issue' do +RSpec.describe 'Moving an issue', feature_category: :team_planning do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb b/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb index 12ab504da14..c5e6901d8f8 100644 --- a/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_confidential_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting an issue as confidential' do +RSpec.describe 'Setting an issue as confidential', feature_category: :team_planning do include GraphqlHelpers let(:current_user) { create(:user) } 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 395a490bfc3..9fce5f8497f 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 @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting issues crm contacts' do +RSpec.describe 'Setting issues crm contacts', feature_category: :service_desk do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb index 8e223b6fdaf..1a5a64e4196 100644 --- a/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting Due Date of an issue' do +RSpec.describe 'Setting Due Date of an issue', feature_category: :team_planning do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb index a81364d37b2..8fc3ad4236d 100644 --- a/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting the escalation status of an incident' do +RSpec.describe 'Setting the escalation status of an incident', feature_category: :incident_management do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb index 435ed0f9eb2..a8025894b1e 100644 --- a/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_locked_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting an issue as locked' do +RSpec.describe 'Setting an issue as locked', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb index cd9d695bd2c..77262c7f64f 100644 --- a/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_severity_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting severity level of an incident' do +RSpec.describe 'Setting severity level of an incident', feature_category: :incident_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb index 1edc1e0553b..6c8e5b1d15d 100644 --- a/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting subscribed status of an issue' do +RSpec.describe 'Setting subscribed status of an issue', feature_category: :team_planning do include GraphqlHelpers it_behaves_like 'a subscribable resource api' do diff --git a/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb b/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb new file mode 100644 index 00000000000..7f6f968b1dd --- /dev/null +++ b/spec/requests/api/graphql/mutations/issues/unlink_alerts_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Unlink alert from an incident', feature_category: :incident_management do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:another_project) { create(:project) } + let_it_be(:internal_alert) { create(:alert_management_alert, project: project) } + let_it_be(:external_alert) { create(:alert_management_alert, project: another_project) } + let_it_be(:incident) do + create(:incident, project: project, alert_management_alerts: [internal_alert, external_alert]) + end + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: incident.iid.to_s, + alert_id: alert_to_unlink.to_global_id.to_s + } + + graphql_mutation(:issue_unlink_alert, variables, + <<-QL.strip_heredoc + clientMutationId + errors + issue { + iid + alertManagementAlerts { + nodes { + id + } + } + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:issue_unlink_alert) + end + + context 'when the user is not allowed to update the incident' do + let(:alert_to_unlink) { internal_alert } + + it 'returns an error' do + error = Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_errors).to include(a_hash_including('message' => error)) + end + end + + context 'when the user is allowed to update the incident' do + before_all do + project.add_developer(user) + end + + shared_examples 'unlinking' do + it 'unlinks the alert from the incident', :aggregate_failures do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expected_response = visible_remainded_alerts.map { |a| { 'id' => a.to_global_id.to_s } } + expect(mutation_response.dig('issue', 'alertManagementAlerts', 'nodes')).to match_array(expected_response) + + expect(incident.reload.alert_management_alerts).to match_array(actual_remainded_alerts) + end + end + + context 'when the alert is internal' do + let(:alert_to_unlink) { internal_alert } + let(:actual_remainded_alerts) { [external_alert] } + let(:visible_remainded_alerts) { [] } # The user cannot fetch external alerts without reading permissions + + it_behaves_like 'unlinking' + end + + context 'when the alert is external' do + let(:alert_to_unlink) { external_alert } + let(:actual_remainded_alerts) { [internal_alert] } + let(:visible_remainded_alerts) { [internal_alert] } + + it_behaves_like 'unlinking' + end + end +end diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb index f38deb426b1..e51c057c182 100644 --- a/spec/requests/api/graphql/mutations/issues/update_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Update of an existing issue' do +RSpec.describe 'Update of an existing issue', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb index b438e1ba881..ab15aa97680 100644 --- a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Importing Jira Users' do +RSpec.describe 'Importing Jira Users', feature_category: :integrations do include JiraIntegrationHelpers include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb index 1508ba31e37..a864bc88afc 100644 --- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Starting a Jira Import' do +RSpec.describe 'Starting a Jira Import', feature_category: :integrations do include JiraIntegrationHelpers include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/labels/create_spec.rb b/spec/requests/api/graphql/mutations/labels/create_spec.rb index d19411f6c1d..607e20af977 100644 --- a/spec/requests/api/graphql/mutations/labels/create_spec.rb +++ b/spec/requests/api/graphql/mutations/labels/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Labels::Create do +RSpec.describe Mutations::Labels::Create, feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb index 3a4508489a1..c954fd50cc4 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new merge request' do +RSpec.describe 'Creation of a new merge request', feature_category: :code_review do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb index 2e4f35cbcde..c41161eff2b 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting assignees of a merge request' do +RSpec.describe 'Setting assignees of a merge request', feature_category: :code_review do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb index 8cec5867aca..364d13291db 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting assignees of a merge request', :assume_throttled do +RSpec.describe 'Setting assignees of a merge request', :assume_throttled, feature_category: :code_review do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb index bea2365eaa6..b48a94fbeb9 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting Draft status of a merge request' do +RSpec.describe 'Setting Draft status of a merge request', feature_category: :code_review do include GraphqlHelpers let(:current_user) { create(:user) } @@ -64,7 +64,7 @@ RSpec.describe 'Setting Draft status of a merge request' do post_graphql_mutation(mutation, current_user: current_user) expect(response).to have_gitlab_http_status(:success) - expect(mutation_response['mergeRequest']['title']).not_to start_with(/draft\:/) + expect(mutation_response['mergeRequest']['title']).not_to start_with(/draft:/) end it 'unmarks the merge request as `Draft`' do diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb index a1a35bc1dcc..d88982c508c 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting locked status of a merge request' do +RSpec.describe 'Setting locked status of a merge request', feature_category: :code_review do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb index d7e2602bd0a..a0f0e45d1fc 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting milestone of a merge request' do +RSpec.describe 'Setting milestone of a merge request', feature_category: :code_review do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb index be786256ef2..a5be2a95c8b 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting reviewers of a merge request', :assume_throttled do +RSpec.describe 'Setting reviewers of a merge request', :assume_throttled, feature_category: :code_review do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb index d90faa605c0..daf1f529847 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Setting subscribed status of a merge request' do +RSpec.describe 'Setting subscribed status of a merge request', feature_category: :code_review do include GraphqlHelpers it_behaves_like 'a subscribable resource api' do diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb index 9ef443af76a..bce57b47aab 100644 --- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb +++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do +RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create, feature_category: :metrics do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb index b956734068c..f505dc25dc0 100644 --- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete do +RSpec.describe Mutations::Metrics::Dashboard::Annotations::Delete, feature_category: :metrics do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb index 567d8799d93..f4f4f34fe29 100644 --- a/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb +++ b/spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating the package settings' do +RSpec.describe 'Updating the package settings', feature_category: :package_registry do include GraphqlHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb index a432fb17a70..3cf78230a4c 100644 --- a/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Adding a DiffNote' do +RSpec.describe 'Adding a DiffNote', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb index 8f2438cb741..0ce239d9ca5 100644 --- a/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Adding an image DiffNote' do +RSpec.describe 'Adding an image DiffNote', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb index 9c3842db31a..00e25909746 100644 --- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Adding a Note' do +RSpec.describe 'Adding a Note', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } @@ -102,6 +102,35 @@ RSpec.describe 'Adding a Note' do it_behaves_like 'a Note mutation with confidential notes' end + + context 'as work item' do + let(:noteable) { create(:work_item, :issue, project: project) } + + context 'when using internal param' do + let(:variables_extra) { { internal: true } } + + it_behaves_like 'a Note mutation with confidential notes' + end + + context 'when using deprecated confidential param' do + let(:variables_extra) { { confidential: true } } + + it_behaves_like 'a Note mutation with confidential notes' + end + + context 'without notes widget' do + let(:variables_extra) { {} } + + before do + stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } }) + stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] }) + end + + it_behaves_like 'a Note mutation that does not create a Note' + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + end + end end context 'when body only contains quick actions' do diff --git a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb index 49f09fadfea..eb45e2aa033 100644 --- a/spec/requests/api/graphql/mutations/notes/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/destroy_spec.rb @@ -2,17 +2,14 @@ require 'spec_helper' -RSpec.describe 'Destroying a Note' do +RSpec.describe 'Destroying a Note', feature_category: :team_planning do include GraphqlHelpers - let!(:note) { create(:note) } - let(:mutation) do - variables = { - id: GitlabSchema.id_from_object(note).to_s - } - - graphql_mutation(:destroy_note, variables) - end + let(:noteable) { create(:work_item, :issue) } + let!(:note) { create(:note, noteable: noteable, project: noteable.project) } + let(:global_note_id) { GitlabSchema.id_from_object(note).to_s } + let(:variables) { { id: global_note_id } } + let(:mutation) { graphql_mutation(:destroy_note, variables) } def mutation_response graphql_mutation_response(:destroy_note) @@ -47,5 +44,31 @@ RSpec.describe 'Destroying a Note' do expect(mutation_response).to have_key('note') expect(mutation_response['note']).to be_nil end + + context 'when note is system' do + let!(:note) { create(:note, :system) } + + it 'does not destroy system note' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.not_to change { Note.count } + end + end + + context 'without notes widget' do + before do + stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } }) + stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] }) + end + + it 'does not update the Note' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to not_change { Note.count } + end + + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + end end end diff --git a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb index c4674155aa0..e9cd27a1e20 100644 --- a/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Repositioning an ImageDiffNote' do +RSpec.describe 'Repositioning an ImageDiffNote', feature_category: :team_planning do include GraphqlHelpers let_it_be(:noteable) { create(:merge_request) } diff --git a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb index cfd0b34b815..a5cd3c8b019 100644 --- a/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating an image DiffNote' do +RSpec.describe 'Updating an image DiffNote', feature_category: :team_planning do include GraphqlHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb index bae5c58abff..dff8a87314b 100644 --- a/spec/requests/api/graphql/mutations/notes/update/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/update/note_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating a Note' do +RSpec.describe 'Updating a Note', feature_category: :team_planning do include GraphqlHelpers let!(:note) { create(:note, note: original_body) } @@ -36,49 +36,32 @@ RSpec.describe 'Updating a Note' do it_behaves_like 'a Note mutation when the given resource id is not for a Note' - it 'updates the Note' do - post_graphql_mutation(mutation, current_user: current_user) - - expect(note.reload.note).to eq(updated_body) - end - - it 'returns the updated Note' do - post_graphql_mutation(mutation, current_user: current_user) - - expect(mutation_response['note']['body']).to eq(updated_body) - end + it_behaves_like 'a Note mutation updates a note successfully' + it_behaves_like 'a Note mutation update with errors' + it_behaves_like 'a Note mutation update only with quick actions' - context 'when there are ActiveRecord validation errors' do - let(:params) { { body: '', confidential: true } } + context 'for work item' do + let(:noteable) { create(:work_item, :issue) } + let(:note) { create(:note, noteable: noteable, project: noteable.project, note: original_body) } - it_behaves_like 'a mutation that returns errors in the response', - errors: ["Note can't be blank", 'Confidential can not be changed for existing notes'] + it_behaves_like 'a Note mutation updates a note successfully' + it_behaves_like 'a Note mutation update with errors' + it_behaves_like 'a Note mutation update only with quick actions' - it 'does not update the Note' do - post_graphql_mutation(mutation, current_user: current_user) - - expect(note.reload.note).to eq(original_body) - expect(note.confidential).to be_falsey - end - - it 'returns the original Note' do - post_graphql_mutation(mutation, current_user: current_user) - - expect(mutation_response['note']['body']).to eq(original_body) - expect(mutation_response['note']['confidential']).to be_falsey - end - end + context 'without notes widget' do + before do + stub_const('WorkItems::Type::BASE_TYPES', { issue: { name: 'NoNotesWidget', enum_value: 0 } }) + stub_const('WorkItems::Type::WIDGETS_FOR_TYPE', { issue: [::WorkItems::Widgets::Description] }) + end - context 'when body only contains quick actions' do - let(:updated_body) { '/close' } + it 'does not update the Note' do + post_graphql_mutation(mutation, current_user: current_user) - it 'returns a nil note and empty errors' do - post_graphql_mutation(mutation, current_user: current_user) + expect(note.reload.note).to eq(original_body) + end - expect(mutation_response).to include( - 'errors' => [], - 'note' => nil - ) + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end end end diff --git a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb index 1fe01af4f1c..d0980a2b43d 100644 --- a/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/bulk_destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying multiple packages' do +RSpec.describe 'Destroying multiple packages', feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb b/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb index 7e00f3ca53a..2540e06be9a 100644 --- a/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating the packages cleanup policy' do +RSpec.describe 'Updating the packages cleanup policy', feature_category: :package_registry do include GraphqlHelpers using RSpec::Parameterized::TableSyntax diff --git a/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb index cd25aba9e00..a4b7001fdd5 100644 --- a/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying a package file' do +RSpec.describe 'Destroying a package file', feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb index 002cd634ebd..cdd05d80fc2 100644 --- a/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying multiple package files' do +RSpec.describe 'Destroying multiple package files', feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb index 2340a6a36d8..86167e7116f 100644 --- a/spec/requests/api/graphql/mutations/packages/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/packages/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying a package' do +RSpec.describe 'Destroying a package', feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb index c7a4cb1ebce..418a0e47a36 100644 --- a/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb +++ b/spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new release asset link' do +RSpec.describe 'Creation of a new release asset link', feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } diff --git a/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb index 57489c82ec2..b6d2c3f691d 100644 --- a/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Deletes a release asset link' do +RSpec.describe 'Deletes a release asset link', feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } diff --git a/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb b/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb index 92b558d4be3..61395cc4042 100644 --- a/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb +++ b/spec/requests/api/graphql/mutations/release_asset_links/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating an existing release asset link' do +RSpec.describe 'Updating an existing release asset link', feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } diff --git a/spec/requests/api/graphql/mutations/releases/create_spec.rb b/spec/requests/api/graphql/mutations/releases/create_spec.rb index 2541072b766..295b8c0e97e 100644 --- a/spec/requests/api/graphql/mutations/releases/create_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creation of a new release' do +RSpec.describe 'Creation of a new release', feature_category: :release_orchestration do include GraphqlHelpers include Presentable diff --git a/spec/requests/api/graphql/mutations/releases/delete_spec.rb b/spec/requests/api/graphql/mutations/releases/delete_spec.rb index eb4f0b594ea..bb398787cc6 100644 --- a/spec/requests/api/graphql/mutations/releases/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Deleting a release' do +RSpec.describe 'Deleting a release', feature_category: :release_orchestration do include GraphqlHelpers include Presentable diff --git a/spec/requests/api/graphql/mutations/releases/update_spec.rb b/spec/requests/api/graphql/mutations/releases/update_spec.rb index 240db764f40..2b88576a70e 100644 --- a/spec/requests/api/graphql/mutations/releases/update_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating an existing release' do +RSpec.describe 'Updating an existing release', feature_category: :release_orchestration do include GraphqlHelpers include Presentable diff --git a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb index 0c034f38dc8..cdd25f8f6ec 100644 --- a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb +++ b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ConfigureSastIac' do +RSpec.describe 'ConfigureSastIac', feature_category: :static_application_security_testing do include GraphqlHelpers let_it_be(:project) { create(:project, :test_repo) } diff --git a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb index 8fa6e44b208..370abf2fe00 100644 --- a/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb +++ b/spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ConfigureSecretDetection' do +RSpec.describe 'ConfigureSecretDetection', feature_category: :secret_detection do include GraphqlHelpers let_it_be(:project) { create(:project, :test_repo) } diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index 264fa5732c3..0b1af2bf628 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Creating a Snippet' do +RSpec.describe 'Creating a Snippet', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb index 1be8ce142ac..09e884d9412 100644 --- a/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/destroy_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Destroying a Snippet' do +RSpec.describe 'Destroying a Snippet', feature_category: :source_code_management do include GraphqlHelpers let(:current_user) { snippet.author } diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb index 77fd6cddc09..9a8c027da8a 100644 --- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Mark snippet as spam' do +RSpec.describe 'Mark snippet as spam', feature_category: :source_code_management do include GraphqlHelpers include AfterNextHelpers diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index 1a5d3620f22..fa087e6773c 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Updating a Snippet' do +RSpec.describe 'Updating a Snippet', feature_category: :source_code_management do include GraphqlHelpers include SessionHelpers @@ -192,11 +192,18 @@ RSpec.describe 'Updating a Snippet' do stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt]) end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do + let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } let(:user) { current_user } + let(:property) { 'g_edit_by_snippet_ide' } let(:namespace) { project.namespace } - let(:category) { 'ide_edit' } - let(:action) { 'g_edit_by_snippet_ide' } + let(:category) { 'Gitlab::UsageDataCounters::EditorUniqueCounter' } + let(:action) { 'ide_edit' } + let(:label) { 'usage_activity_by_stage_monthly.create.action_monthly_active_users_ide_edit' } + let(:context) do + [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: event_name).to_context] + end + let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } end end diff --git a/spec/requests/api/graphql/mutations/timelogs/create_spec.rb b/spec/requests/api/graphql/mutations/timelogs/create_spec.rb index eea04b89783..42249818a92 100644 --- a/spec/requests/api/graphql/mutations/timelogs/create_spec.rb +++ b/spec/requests/api/graphql/mutations/timelogs/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a timelog' do +RSpec.describe 'Create a timelog', feature_category: :team_planning do include GraphqlHelpers let_it_be(:author) { create(:user) } @@ -11,14 +11,6 @@ RSpec.describe 'Create a timelog' do let(:current_user) { nil } let(:users_container) { project } - let(:mutation) do - graphql_mutation(:timelogCreate, { - 'time_spent' => time_spent, - 'spent_at' => '2022-07-08', - 'summary' => 'Test summary', - 'issuable_id' => issuable.to_global_id.to_s - }) - end let(:mutation_response) { graphql_mutation_response(:timelog_create) } diff --git a/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb index d304bfbdf00..d04b4d193e6 100644 --- a/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Delete a timelog' do +RSpec.describe 'Delete a timelog', feature_category: :team_planning do include GraphqlHelpers let_it_be(:author) { create(:user) } let_it_be(:project) { create(:project, :public) } diff --git a/spec/requests/api/graphql/mutations/todos/create_spec.rb b/spec/requests/api/graphql/mutations/todos/create_spec.rb index aca00519682..5d7ecb3c927 100644 --- a/spec/requests/api/graphql/mutations/todos/create_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a todo' do +RSpec.describe 'Create a todo', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb index dc20fde8e3c..c611c6ee2a1 100644 --- a/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Marking all todos done' do +RSpec.describe 'Marking all todos done', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb index 7f5ea71c760..60700d8024c 100644 --- a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Marking todos done' do +RSpec.describe 'Marking todos done', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb index 4316bd060c1..9daa243cf8e 100644 --- a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Restoring many Todos' do +RSpec.describe 'Restoring many Todos', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/todos/restore_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_spec.rb index d995191c97e..868298763ec 100644 --- a/spec/requests/api/graphql/mutations/todos/restore_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/restore_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Restoring Todos' do +RSpec.describe 'Restoring Todos', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/uploads/delete_spec.rb b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb index 2d1b33cc086..08dbbe23b6b 100644 --- a/spec/requests/api/graphql/mutations/uploads/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Delete an upload' do +RSpec.describe 'Delete an upload', feature_category: :navigation do include GraphqlHelpers let_it_be(:group) { create(:group) } 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 28a46583d2a..eb35d310760 100644 --- a/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb +++ b/spec/requests/api/graphql/mutations/user_callouts/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a user callout' do +RSpec.describe 'Create a user callout', feature_category: :navigation do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb index e1c7fd9d60d..31d17401b9e 100644 --- a/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb +++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Mutations::UserPreferences::Update do +RSpec.describe Mutations::UserPreferences::Update, feature_category: :users do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb index c6a980b5cef..97bf060356a 100644 --- a/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Create a work item from a task in a work item's description" do +RSpec.describe "Create a work item from a task in a work item's description", feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_spec.rb index be3917316c3..16f78b67b5c 100644 --- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Create a work item' do +RSpec.describe 'Create a work item', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } @@ -123,7 +123,7 @@ RSpec.describe 'Create a work item' do post_graphql_mutation(mutation, current_user: current_user) expect(mutation_response['errors']) - .to contain_exactly(/cannot be added: only Issue and Incident can be parent of Task./) + .to contain_exactly(/cannot be added: is not allowed to add this type of parent/) expect(mutation_response['workItem']).to be_nil end end diff --git a/spec/requests/api/graphql/mutations/work_items/delete_spec.rb b/spec/requests/api/graphql/mutations/work_items/delete_spec.rb index 0a84225a7ab..e25ff5613e4 100644 --- a/spec/requests/api/graphql/mutations/work_items/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Delete a work item' do +RSpec.describe 'Delete a work item', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb index c44939c8d54..b1828de046f 100644 --- a/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe "Delete a task in a work item's description" do +RSpec.describe "Delete a task in a work item's description", feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb index 96736457f26..14cb18d04b8 100644 --- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Update a work item' do +RSpec.describe 'Update a work item', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group) } @@ -339,7 +339,7 @@ RSpec.describe 'Update a work item' do let_it_be(:invalid_parent) { create(:work_item, :task, project: project) } context 'when parent work item type is invalid' do - let(:error) { "#{work_item.to_reference} cannot be added: only Issue and Incident can be parent of Task." } + let(:error) { "#{work_item.to_reference} cannot be added: is not allowed to add this type of parent" } let(:input) do { 'hierarchyWidget' => { 'parentId' => invalid_parent.to_global_id.to_s }, 'title' => 'new title' } end @@ -450,7 +450,7 @@ RSpec.describe 'Update a work item' do let(:input) { { 'hierarchyWidget' => { 'childrenIds' => children_ids } } } let(:error) do - "#{invalid_child.to_reference} cannot be added: only Task can be assigned as a child in hierarchy." + "#{invalid_child.to_reference} cannot be added: is not allowed to add this type of parent" end context 'when child work item type is invalid' do @@ -632,7 +632,7 @@ RSpec.describe 'Update a work item' do end context 'when unsupported widget input is sent' do - let_it_be(:test_case) { create(:work_item_type, :default, :test_case, name: 'some_test_case_name') } + let_it_be(:test_case) { create(:work_item_type, :default, :test_case) } let_it_be(:work_item) { create(:work_item, work_item_type: test_case, project: project) } let(:input) do @@ -642,7 +642,7 @@ RSpec.describe 'Update a work item' do end it_behaves_like 'a mutation that returns top-level errors', - errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"] + errors: ["Following widget keys are not supported by Test Case type: [:hierarchy_widget]"] end end end diff --git a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb index 55285be5a5d..999c685ac6a 100644 --- a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Update a work item task' do +RSpec.describe 'Update a work item task', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/namespace/package_settings_spec.rb b/spec/requests/api/graphql/namespace/package_settings_spec.rb index 42fd07dbdc7..bd441032626 100644 --- a/spec/requests/api/graphql/namespace/package_settings_spec.rb +++ b/spec/requests/api/graphql/namespace/package_settings_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting namespace package settings in a namespace' do +RSpec.describe 'getting namespace package settings in a namespace', feature_category: :package_registry do include GraphqlHelpers let_it_be(:package_settings) { create(:namespace_package_setting) } diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb index d5410f1a7cb..4e12da3e3ab 100644 --- a/spec/requests/api/graphql/namespace/projects_spec.rb +++ b/spec/requests/api/graphql/namespace/projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting projects' do +RSpec.describe 'getting projects', feature_category: :projects do include GraphqlHelpers let(:group) { create(:group) } diff --git a/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb b/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb index 8d8a0baae36..cee698d6dc5 100644 --- a/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb +++ b/spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'rendering namespace statistics' do +RSpec.describe 'rendering namespace statistics', feature_category: :metrics do include GraphqlHelpers let(:namespace) { user.namespace } diff --git a/spec/requests/api/graphql/namespace_query_spec.rb b/spec/requests/api/graphql/namespace_query_spec.rb index e17469901c6..d12a3875ebf 100644 --- a/spec/requests/api/graphql/namespace_query_spec.rb +++ b/spec/requests/api/graphql/namespace_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query' do +RSpec.describe 'Query', feature_category: :subgroups do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb index 89c01d44771..dd61582b055 100644 --- a/spec/requests/api/graphql/packages/composer_spec.rb +++ b/spec/requests/api/graphql/packages/composer_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'package details' do +RSpec.describe 'package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb index 7ad85edecef..b8226c482ac 100644 --- a/spec/requests/api/graphql/packages/conan_spec.rb +++ b/spec/requests/api/graphql/packages/conan_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'conan package details' do +RSpec.describe 'conan package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/packages/helm_spec.rb b/spec/requests/api/graphql/packages/helm_spec.rb index 79a589e2dc2..65b3f80d6df 100644 --- a/spec/requests/api/graphql/packages/helm_spec.rb +++ b/spec/requests/api/graphql/packages/helm_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'helm package details' do +RSpec.describe 'helm package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb index b7f39efcf73..26c45ada4a1 100644 --- a/spec/requests/api/graphql/packages/maven_spec.rb +++ b/spec/requests/api/graphql/packages/maven_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'maven package details' do +RSpec.describe 'maven package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb index 7de132d1574..1c3af46909e 100644 --- a/spec/requests/api/graphql/packages/nuget_spec.rb +++ b/spec/requests/api/graphql/packages/nuget_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'nuget package details' do +RSpec.describe 'nuget package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 02a3206f587..42927634119 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'package details' do +RSpec.describe 'package details', feature_category: :package_registry do include GraphqlHelpers let_it_be_with_reload(:group) { create(:group) } @@ -226,5 +226,16 @@ RSpec.describe 'package details' do end end end + + context 'with package that has no default status' do + before do + composer_package.update!(status: :error) + subject + end + + it "does not return package's details" do + expect(package_details).to be_nil + end + end end end diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb index c0e589f3597..4441f04dabb 100644 --- a/spec/requests/api/graphql/packages/pypi_spec.rb +++ b/spec/requests/api/graphql/packages/pypi_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'pypi package details' do +RSpec.describe 'pypi package details', feature_category: :package_registry do include GraphqlHelpers include_context 'package details setup' diff --git a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb index a59402208ec..c4843c3cf97 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Alert Management Alert Assignees' do +RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :projects do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb index 29896c16f5b..3c9ec4fb60b 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Alert Management Alert Issue' do +RSpec.describe 'getting Alert Management Alert Issue', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb index 352a94cfc1d..b430fdeb18f 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Alert Management Alert Assignees' do +RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :projects do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb index 72d185144ef..16dd0dfcfcb 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Alert Management Alert Notes' do +RSpec.describe 'getting Alert Management Alert Notes', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb b/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb index ca58079fdfe..ad4361dfa50 100644 --- a/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting Alert Management Alert Assignees' do +RSpec.describe 'getting Alert Management Alert Assignees', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb index ecd93d169d3..7ce5bf23357 100644 --- a/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting Alert Management Alert counts by status' do +RSpec.describe 'getting Alert Management Alert counts by status', feature_category: :incident_management do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb index fe77d9dc86d..304edfbf4e4 100644 --- a/spec/requests/api/graphql/project/alert_management/alerts_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/alerts_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting Alert Management Alerts' do +RSpec.describe 'getting Alert Management Alerts', feature_category: :incident_management do include GraphqlHelpers let_it_be(:payload) { { 'custom' => { 'alert' => 'payload' }, 'runbook' => 'runbook' } } @@ -59,6 +59,7 @@ RSpec.describe 'getting Alert Management Alerts' do it 'returns the correct properties of the alerts' do expect(first_alert).to include( + 'id' => triggered_alert.to_global_id.to_s, 'iid' => triggered_alert.iid.to_s, 'title' => triggered_alert.title, 'description' => triggered_alert.description, @@ -80,6 +81,7 @@ RSpec.describe 'getting Alert Management Alerts' do ) expect(second_alert).to include( + 'id' => resolved_alert.to_global_id.to_s, 'iid' => resolved_alert.iid.to_s, 'status' => 'RESOLVED', 'endedAt' => resolved_alert.ended_at.strftime('%Y-%m-%dT%H:%M:%SZ') diff --git a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb index 773922c1864..e8d19513a4e 100644 --- a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting Alert Management Integrations' do +RSpec.describe 'getting Alert Management Integrations', feature_category: :integrations do include ::Gitlab::Routing include GraphqlHelpers diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb index 58d10ade8cf..7b1b95eaf58 100644 --- a/spec/requests/api/graphql/project/base_service_spec.rb +++ b/spec/requests/api/graphql/project/base_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query Jira service' do +RSpec.describe 'query Jira service', feature_category: :authentication_and_authorization do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb index a80f683ea93..d7672cc5116 100644 --- a/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb +++ b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -RSpec.describe 'getting merge access levels for a branch protection' do - include_examples 'perform graphql requests for AccessLevel type objects', :merge +RSpec.describe 'getting merge access levels for a branch protection', feature_category: :source_code_management do + it_behaves_like 'a GraphQL query for access levels', :merge end diff --git a/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb index cfdaf1096c3..65b9bc93a6b 100644 --- a/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb +++ b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -RSpec.describe 'getting push access levels for a branch protection' do - include_examples 'perform graphql requests for AccessLevel type objects', :push +RSpec.describe 'getting push access levels for a branch protection', feature_category: :source_code_management do + it_behaves_like 'a GraphQL query for access levels', :push end diff --git a/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb index 8a3f546ef95..560819cb989 100644 --- a/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb +++ b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting branch protection for a branch rule' do +RSpec.describe 'getting branch protection for a branch rule', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb index ed866305445..7f6a66e2377 100644 --- a/spec/requests/api/graphql/project/branch_rules_spec.rb +++ b/spec/requests/api/graphql/project/branch_rules_spec.rb @@ -2,21 +2,11 @@ require 'spec_helper' -RSpec.describe 'getting list of branch rules for a project' do +RSpec.describe 'getting list of branch rules for a project', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:current_user) { create(:user) } - let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first } - let_it_be(:branch_name_b) { 'diff-*' } - let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] } - let_it_be(:branch_rule_a) do - create(:protected_branch, project: project, name: branch_name_a) - end - - let_it_be(:branch_rule_b) do - create(:protected_branch, project: project, name: branch_name_b) - end let(:branch_rules_data) { graphql_data_at('project', 'branchRules', 'edges') } let(:variables) { { path: project.full_path } } @@ -61,39 +51,39 @@ RSpec.describe 'getting list of branch rules for a project' do end describe 'queries' do + include_context 'when user tracking is disabled' + + let(:query) do + <<~GQL + query($path: ID!) { + project(fullPath: $path) { + branchRules { + nodes { + matchingBranchesCount + } + } + } + } + GQL + end + before do - # rubocop:disable RSpec/AnyInstanceOf - allow_any_instance_of(User).to receive(:update_tracked_fields!) - allow_any_instance_of(Users::ActivityService).to receive(:execute) - # rubocop:enable RSpec/AnyInstanceOf + create(:protected_branch, project: project) allow_next_instance_of(Resolvers::ProjectResolver) do |resolver| allow(resolver).to receive(:resolve) .with(full_path: project.full_path) .and_return(project) end allow(project.repository).to receive(:branch_names).and_call_original - allow(project.repository.gitaly_ref_client).to receive(:branch_names).and_call_original end - it 'matching_branches_count avoids N+1 queries' do - query = <<~GQL - query($path: ID!) { - project(fullPath: $path) { - branchRules { - nodes { - matchingBranchesCount - } - } - } - } - GQL - - control = ActiveRecord::QueryRecorder.new do + it 'avoids N+1 queries', :use_sql_query_cache, :aggregate_failures do + control = ActiveRecord::QueryRecorder.new(skip_cached: false) do post_graphql(query, current_user: current_user, variables: variables) end # Verify the response includes the field - expect_n_matching_branches_count_fields(2) + expect_n_matching_branches_count_fields(1) create(:protected_branch, project: project) create(:protected_branch, name: '*', project: project) @@ -102,10 +92,8 @@ RSpec.describe 'getting list of branch rules for a project' do post_graphql(query, current_user: current_user, variables: variables) end.not_to exceed_all_query_limit(control) + expect_n_matching_branches_count_fields(3) expect(project.repository).to have_received(:branch_names).at_least(2).times - expect(project.repository.gitaly_ref_client).to have_received(:branch_names).once - - expect_n_matching_branches_count_fields(4) end def expect_n_matching_branches_count_fields(count) @@ -118,21 +106,28 @@ RSpec.describe 'getting list of branch rules for a project' do end describe 'response' do + let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first } + let_it_be(:branch_name_b) { 'diff-*' } + let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] } + let_it_be(:branch_rule_a) do + create(:protected_branch, project: project, name: branch_name_a, id: 9999) + end + + let_it_be(:branch_rule_b) do + create(:protected_branch, project: project, name: branch_name_b, id: 10000) + end + + # branchRules are returned in reverse order, newest first, sorted by primary_key. + let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') } + let(:branch_rule_a_data) { branch_rules_data.dig(1, 'node') } + before do post_graphql(query, current_user: current_user, variables: variables) end it_behaves_like 'a working graphql query' - it 'includes all fields', :aggregate_failures do - # Responses will be sorted alphabetically. Branch names for this spec - # come from an external constant so we check which is first - br_a_idx = branch_name_a < branch_name_b ? 0 : 1 - br_b_idx = 1 - br_a_idx - - branch_rule_a_data = branch_rules_data.dig(br_a_idx, 'node') - branch_rule_b_data = branch_rules_data.dig(br_b_idx, 'node') - + it 'includes all fields', :use_sql_query_cache, :aggregate_failures do expect(branch_rule_a_data['name']).to eq(branch_name_a) expect(branch_rule_a_data['isDefault']).to be(true).or be(false) expect(branch_rule_a_data['branchProtection']).to be_present diff --git a/spec/requests/api/graphql/project/cluster_agents_spec.rb b/spec/requests/api/graphql/project/cluster_agents_spec.rb index bb716cf2849..0881eb9cdc3 100644 --- a/spec/requests/api/graphql/project/cluster_agents_spec.rb +++ b/spec/requests/api/graphql/project/cluster_agents_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project.cluster_agents' do +RSpec.describe 'Project.cluster_agents', feature_category: :kubernetes_management do include GraphqlHelpers let_it_be(:project) { create(:project, :public) } diff --git a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb index e3ea9e46353..e484e0618aa 100644 --- a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb +++ b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting a repository in a project' do +RSpec.describe 'getting a repository in a project', feature_category: :container_registry do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index 01b117a89d8..7ccf8a6f5bf 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting container repositories in a project' do +RSpec.describe 'getting container repositories in a project', feature_category: :container_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/project/deployment_spec.rb b/spec/requests/api/graphql/project/deployment_spec.rb index e5ef7bcafbf..e3ec33ec131 100644 --- a/spec/requests/api/graphql/project/deployment_spec.rb +++ b/spec/requests/api/graphql/project/deployment_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project Deployment query' do +RSpec.describe 'Project Deployment query', feature_category: :continuous_delivery do let_it_be(:project) { create(:project, :private, :repository) } let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } } let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } } diff --git a/spec/requests/api/graphql/project/environments_spec.rb b/spec/requests/api/graphql/project/environments_spec.rb index e5b6aebbf2c..618f591affa 100644 --- a/spec/requests/api/graphql/project/environments_spec.rb +++ b/spec/requests/api/graphql/project/environments_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project Environments query' do +RSpec.describe 'Project Environments query', feature_category: :continuous_delivery do include GraphqlHelpers let_it_be(:project) { create(:project, :private, :repository) } @@ -47,6 +47,60 @@ RSpec.describe 'Project Environments query' do expect(environment_data['environmentType']).to eq(production.environment_type) end + describe 'user permissions' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environment(name: "#{production.name}") { + userPermissions { + updateEnvironment + destroyEnvironment + stopEnvironment + } + } + } + } + ) + end + + it 'returns user permissions of the environment', :aggregate_failures do + subject + + permission_data = graphql_data.dig('project', 'environment', 'userPermissions') + expect(permission_data['updateEnvironment']).to eq(true) + expect(permission_data['destroyEnvironment']).to eq(false) + expect(permission_data['stopEnvironment']).to eq(true) + end + + context 'when fetching user permissions for multiple environments' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + environments { + nodes { + userPermissions { + updateEnvironment + destroyEnvironment + stopEnvironment + } + } + } + } + } + ) + end + + it 'limits the result', :aggregate_failures do + subject + + expect_graphql_errors_to_include('"userPermissions" field can be requested only ' \ + 'for 1 Environment(s) at a time.') + end + end + end + describe 'last deployments of environments' do ::Deployment.statuses.each do |status, _| let_it_be(:"production_#{status}_deployment") do @@ -130,4 +184,81 @@ RSpec.describe 'Project Environments query' do expect(multi).not_to exceed_query_limit(baseline) end end + + describe 'nested environments' do + let_it_be(:testing1) { create(:environment, name: 'testing/one', project: project) } + let_it_be(:testing2) { create(:environment, name: 'testing/two', project: project) } + + context 'with query' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + nestedEnvironments { + nodes { + name + size + environment { + name + path + } + } + } + } + } + ) + end + + it 'can fetch nested environments' do + subject + + nested_envs = graphql_data.dig('project', 'nestedEnvironments', 'nodes') + expect(nested_envs.count).to be(3) + expect(nested_envs.pluck('name')).to match_array(%w[production staging testing]) + expect(nested_envs.pluck('size')).to match_array([1, 1, 2]) + expect(nested_envs[0].dig('environment', 'name')).to eq(production.name) + end + + context 'when user is guest' do + let(:user) { create(:user).tap { |u| project.add_guest(u) } } + + it 'returns nothing' do + subject + + nested_envs = graphql_data.dig('project', 'nestedEnvironments', 'nodes') + + expect(nested_envs).to be_nil + end + end + end + + context 'when using pagination' do + let(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + nestedEnvironments(first: 1) { + nodes { + name + } + pageInfo { + hasPreviousPage + startCursor + endCursor + hasNextPage + } + } + } + } + ) + end + + it 'supports pagination' do + subject + nested_envs = graphql_data.dig('project', 'nestedEnvironments') + expect(nested_envs['nodes'].count).to eq(1) + expect(nested_envs.dig('pageInfo', 'hasNextPage')).to be_truthy + end + end + end end diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb index 2fe5fb593fe..e1a8304dce6 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting a detailed sentry error' do +RSpec.describe 'getting a detailed sentry error', feature_category: :error_tracking do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb index 3ca0e35882a..2abb1f62ea9 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_errors_request_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'sentry errors requests' do +RSpec.describe 'sentry errors requests', feature_category: :error_tracking do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/fork_details_spec.rb b/spec/requests/api/graphql/project/fork_details_spec.rb new file mode 100644 index 00000000000..efd48b00833 --- /dev/null +++ b/spec/requests/api/graphql/project/fork_details_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting project fork details', feature_category: :source_code_management do + include GraphqlHelpers + include ProjectForksHelper + + let_it_be(:project) { create(:project, :public, :repository_private, :repository) } + let_it_be(:current_user) { create(:user, maintainer_projects: [project]) } + let_it_be(:forked_project) { fork_project(project, current_user, repository: true) } + + let(:queried_project) { forked_project } + + let(:query) do + graphql_query_for(:project, + { full_path: queried_project.full_path }, <<~QUERY + forkDetails(ref: "feature"){ + ahead + behind + } + QUERY + ) + end + + it 'returns fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to eq( + { 'ahead' => 1, 'behind' => 29 } + ) + end + + context 'when a project is not a fork' do + let(:queried_project) { project } + + it 'does not return fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to be_nil + end + end + + context 'when a user cannot read the code' do + let_it_be(:current_user) { create(:user) } + + before do + forked_project.update!({ + repository_access_level: 'private', + merge_requests_access_level: 'private' + }) + end + + it 'does not return fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/project/fork_targets_spec.rb b/spec/requests/api/graphql/project/fork_targets_spec.rb index b21a11ff4dc..f2a3901b2c6 100644 --- a/spec/requests/api/graphql/project/fork_targets_spec.rb +++ b/spec/requests/api/graphql/project/fork_targets_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a list of fork targets for a project' do +RSpec.describe 'getting a list of fork targets for a project', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb index e7534945e7a..1d4f52a8ace 100644 --- a/spec/requests/api/graphql/project/grafana_integration_spec.rb +++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Getting Grafana Integration' do +RSpec.describe 'Getting Grafana Integration', feature_category: :metrics do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb index 544d2d7bd95..7587b227d9f 100644 --- a/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb +++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting incident timeline events' do +RSpec.describe 'getting incident timeline events', feature_category: :incident_management do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb index 0444ce43c22..5ccf5c1999a 100644 --- a/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb +++ b/spec/requests/api/graphql/project/issue/design_collection/version_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)' do +RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)', +feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers @@ -67,7 +68,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) query_graphql_field(:design_at_version, dav_params, 'id filename') end - shared_examples :finds_dav do + shared_examples 'finds dav' do it 'finds all the designs as of the given version' do post_query @@ -88,19 +89,19 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) context 'by ID' do let(:dav_params) { { id: global_id_of(design_at_version) } } - include_examples :finds_dav + include_examples 'finds dav' end context 'by filename' do let(:dav_params) { { filename: design.filename } } - include_examples :finds_dav + include_examples 'finds dav' end context 'by design_id' do let(:dav_params) { { design_id: global_id_of(design) } } - include_examples :finds_dav + include_examples 'finds dav' end end diff --git a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb index 46fd65db1c5..a15e4c1e792 100644 --- a/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb +++ b/spec/requests/api/graphql/project/issue/design_collection/versions_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting versions related to an issue' do +RSpec.describe 'Getting versions related to an issue', feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb index 965534654ea..3765899daf2 100644 --- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting designs related to an issue' do +RSpec.describe 'Getting designs related to an issue', feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb index 3b1eb0b4b02..69ca7030292 100644 --- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting designs related to an issue' do +RSpec.describe 'Getting designs related to an issue', feature_category: :design_management do include GraphqlHelpers include DesignManagementTestHelpers diff --git a/spec/requests/api/graphql/project/issue/notes_spec.rb b/spec/requests/api/graphql/project/issue/notes_spec.rb index 97f5261ef1d..0c7f042510e 100644 --- a/spec/requests/api/graphql/project/issue/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting notes for an issue' do +RSpec.describe 'getting notes for an issue', feature_category: :team_planning do include GraphqlHelpers let(:noteable) { create(:issue) } diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb index 2415e9ef60f..bc90f9e89e6 100644 --- a/spec/requests/api/graphql/project/issue_spec.rb +++ b/spec/requests/api/graphql/project/issue_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).issue(iid)' do +RSpec.describe 'Query.project(fullPath).issue(iid)', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index 214165cb171..ec5e3c6f0de 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -2,44 +2,92 @@ require 'spec_helper' -RSpec.describe 'getting an issue list for a project' do +RSpec.describe 'getting an issue list for a project', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :repository, :public, group: group) } let_it_be(:current_user) { create(:user) } let_it_be(:another_user) { create(:user).tap { |u| group.add_reporter(u) } } - let_it_be(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } - let_it_be(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } + let_it_be(:milestone1) { create(:milestone, project: project, due_date: 10.days.from_now) } + let_it_be(:milestone2) { create(:milestone, project: project, due_date: 20.days.from_now) } + let_it_be(:milestone3) { create(:milestone, project: project, due_date: 30.days.from_now) } + let_it_be(:milestone4) { create(:milestone, project: project, due_date: 40.days.from_now) } let_it_be(:priority1) { create(:label, project: project, priority: 1) } let_it_be(:priority2) { create(:label, project: project, priority: 5) } let_it_be(:priority3) { create(:label, project: project, priority: 10) } - let_it_be(:issue_a, reload: true) { create(:issue, project: project, discussion_locked: true, labels: [priority3]) } - let_it_be(:issue_b, reload: true) { create(:issue, :with_alert, project: project, title: 'title matching issue i') } - let_it_be(:issue_c) { create(:issue, project: project, labels: [priority1], milestone: late_milestone) } - let_it_be(:issue_d) { create(:issue, project: project, labels: [priority2]) } - let_it_be(:issue_e) { create(:issue, project: project, milestone: early_milestone) } - let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] } + let_it_be(:issue_a) do + create( + :issue, + project: project, + discussion_locked: true, + labels: [priority3], + relative_position: 1000, + milestone: milestone4 + ) + end - let(:issue_a_gid) { issue_a.to_global_id.to_s } - let(:issue_b_gid) { issue_b.to_global_id.to_s } - let(:issues_data) { graphql_data['project']['issues']['nodes'] } - let(:issue_filter_params) { {} } + let_it_be(:issue_b) do + create( + :issue, + :with_alert, + project: project, + title: 'title matching issue i', + due_date: 3.days.ago, + relative_position: 3000, + labels: [priority2, priority3], + milestone: milestone1 + ) + end - let(:fields) do - <<~QUERY - nodes { - #{all_graphql_fields_for('issues'.classify)} - } - QUERY + let_it_be(:issue_c) do + create( + :issue, + project: project, + labels: [priority1], + milestone: milestone2, + due_date: 1.day.ago, + relative_position: nil + ) + end + + let_it_be(:issue_d) do + create(:issue, + project: project, + labels: [priority2], + due_date: 3.days.from_now, + relative_position: 5000, + milestone: milestone3 + ) + end + + let_it_be(:issue_e) do + create( + :issue, + :confidential, + project: project, + due_date: 1.day.from_now, + relative_position: nil + ) end + let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] } + + let(:issue_nodes_path) { %w[project issues nodes] } + let(:issue_filter_params) { {} } + # All new specs should be added to the shared example if the change also # affects the `issues` query at the root level of the API. # Shared example also used in spec/requests/api/graphql/issues_spec.rb it_behaves_like 'graphql issue list request spec' do - subject(:post_query) { post_graphql(query, current_user: current_user) } + let_it_be(:external_user) { create(:user) } + + let(:public_projects) { [project] } + + before_all do + group.add_developer(current_user) + end # filters let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] } @@ -50,24 +98,31 @@ RSpec.describe 'getting an issue list for a project' do let(:unlocked_discussion_issues) { [issue_b, issue_c, issue_d, issue_e] } let(:search_title_term) { 'matching issue' } let(:title_search_issue) { issue_b } + let(:confidential_issues) { [issue_e] } + let(:non_confidential_issues) { [issue_a, issue_b, issue_c, issue_d] } + let(:public_non_confidential_issues) { non_confidential_issues } # sorting let(:data_path) { [:project, :issues] } - let(:expected_severity_sorted_asc) { [issue_c, issue_a, issue_b, issue_e, issue_d] } - let(:expected_priority_sorted_asc) { [issue_e, issue_c, issue_d, issue_a, issue_b] } - let(:expected_priority_sorted_desc) { [issue_c, issue_e, issue_a, issue_d, issue_b] } + let(:expected_priority_sorted_asc) { [issue_b, issue_c, issue_d, issue_a, issue_e] } + let(:expected_priority_sorted_desc) { [issue_a, issue_d, issue_c, issue_b, issue_e] } + let(:expected_due_date_sorted_desc) { [issue_d, issue_e, issue_c, issue_b, issue_a] } + let(:expected_due_date_sorted_asc) { [issue_b, issue_c, issue_e, issue_d, issue_a] } + let(:expected_relative_position_sorted_asc) { [issue_a, issue_b, issue_d, issue_c, issue_e] } + let(:expected_label_priority_sorted_asc) { [issue_c, issue_d, issue_b, issue_a, issue_e] } + let(:expected_label_priority_sorted_desc) { [issue_a, issue_d, issue_b, issue_c, issue_e] } + let(:expected_milestone_sorted_asc) { [issue_b, issue_c, issue_d, issue_a, issue_e] } + let(:expected_milestone_sorted_desc) { [issue_a, issue_d, issue_c, issue_b, issue_e] } + + # N+1 queries + let(:same_project_issue1) { issue_a } + let(:same_project_issue2) { issue_b } before_all do issue_a.assignee_ids = current_user.id issue_b.assignee_ids = another_user.id create(:award_emoji, :upvote, user: current_user, awardable: issue_a) - - # severity sorting - create(:issuable_severity, issue: issue_a, severity: :unknown) - create(:issuable_severity, issue: issue_b, severity: :low) - create(:issuable_severity, issue: issue_d, severity: :critical) - create(:issuable_severity, issue: issue_e, severity: :high) end def pagination_query(params) @@ -77,591 +132,10 @@ RSpec.describe 'getting an issue list for a project' do query_graphql_field(:issues, params, "#{page_info} nodes { id }") ) end - end - - context 'when limiting the number of results' do - let(:query) do - <<~GQL - query($path: ID!, $n: Int) { - project(fullPath: $path) { - issues(first: $n) { #{fields} } - } - } - GQL - end - - let(:issue_limit) { 1 } - let(:variables) do - { path: project.full_path, n: issue_limit } - end - - it_behaves_like 'a working graphql query' do - before do - post_graphql(query, current_user: current_user, variables: variables) - end - - it 'only returns N issues' do - expect(issues_data.size).to eq(issue_limit) - end - end - - context 'when no limit is provided' do - let(:issue_limit) { nil } - - it 'returns all issues' do - post_graphql(query, current_user: current_user, variables: variables) - - expect(issues_data.size).to be > 1 - end - end - - it 'is expected to check permissions on the first issue only' do - allow(Ability).to receive(:allowed?).and_call_original - # Newest first, we only want to see the newest checked - expect(Ability).not_to receive(:allowed?).with(current_user, :read_issue, issues.first) - - post_graphql(query, current_user: current_user, variables: variables) - end - end - - context 'when the user does not have access to the issue' do - it 'returns nil' do - project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) - - post_graphql(query) - - expect(issues_data).to eq([]) - end - end - - context 'when there is a confidential issue' do - let_it_be(:confidential_issue) do - create(:issue, :confidential, project: project) - end - - let(:confidential_issue_gid) { confidential_issue.to_global_id.to_s } - - context 'when the user cannot see confidential issues' do - it 'returns issues without confidential issues' do - post_graphql(query, current_user: current_user) - - expect(issues_data.size).to eq(5) - - issues_data.each do |issue| - expect(issue['confidential']).to eq(false) - end - end - - context 'filtering for confidential issues' do - let(:issue_filter_params) { { confidential: true } } - - it 'returns no issues' do - post_graphql(query, current_user: current_user) - - expect(issues_data.size).to eq(0) - end - end - - context 'filtering for non-confidential issues' do - let(:issue_filter_params) { { confidential: false } } - - it 'returns correctly filtered issues' do - post_graphql(query, current_user: current_user) - - expect(issue_ids).to match_array(issues.map { |i| i.to_gid.to_s }) - end - end - end - - context 'when the user can see confidential issues' do - before do - project.add_developer(current_user) - end - - it 'returns issues with confidential issues' do - post_graphql(query, current_user: current_user) - - expect(issues_data.size).to eq(6) - - confidentials = issues_data.map do |issue| - issue['confidential'] - end - - expect(confidentials).to contain_exactly(true, false, false, false, false, false) - end - - context 'filtering for confidential issues' do - let(:issue_filter_params) { { confidential: true } } - - it 'returns correctly filtered issues' do - post_graphql(query, current_user: current_user) - - expect(issue_ids).to contain_exactly(confidential_issue_gid) - end - end - - context 'filtering for non-confidential issues' do - let(:issue_filter_params) { { confidential: false } } - - it 'returns correctly filtered issues' do - post_graphql(query, current_user: current_user) - - expect(issue_ids).to match_array([issue_a, issue_b, issue_c, issue_d, issue_e].map { |i| i.to_gid.to_s }) - end - end - end - end - - describe 'sorting and pagination' do - let_it_be(:sort_project) { create(:project, :public) } - let_it_be(:data_path) { [:project, :issues] } - - def pagination_query(params) - graphql_query_for( - :project, - { full_path: sort_project.full_path }, - query_graphql_field(:issues, params, "#{page_info} nodes { iid }") - ) - end - def pagination_results_data(data) - data.map { |issue| issue['iid'].to_i } + def post_query(request_user = current_user) + post_graphql(query, current_user: request_user) end - - # rubocop:disable RSpec/MultipleMemoizedHelpers - context 'when sorting by due date' do - let_it_be(:due_issue1) { create(:issue, project: sort_project, due_date: 3.days.from_now) } - let_it_be(:due_issue2) { create(:issue, project: sort_project, due_date: nil) } - let_it_be(:due_issue3) { create(:issue, project: sort_project, due_date: 2.days.ago) } - let_it_be(:due_issue4) { create(:issue, project: sort_project, due_date: nil) } - let_it_be(:due_issue5) { create(:issue, project: sort_project, due_date: 1.day.ago) } - - context 'when ascending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :DUE_DATE_ASC } - let(:first_param) { 2 } - let(:all_records) { [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid] } - end - end - - context 'when descending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :DUE_DATE_DESC } - let(:first_param) { 2 } - let(:all_records) { [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid] } - end - end - end - - context 'when sorting by relative position' do - let_it_be(:relative_issue1) { create(:issue, project: sort_project, relative_position: 2000) } - let_it_be(:relative_issue2) { create(:issue, project: sort_project, relative_position: nil) } - let_it_be(:relative_issue3) { create(:issue, project: sort_project, relative_position: 1000) } - let_it_be(:relative_issue4) { create(:issue, project: sort_project, relative_position: nil) } - let_it_be(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) } - - context 'when ascending' do - it_behaves_like 'sorted paginated query', is_reversible: true do - let(:sort_param) { :RELATIVE_POSITION_ASC } - let(:first_param) { 2 } - let(:all_records) do - [ - relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, - relative_issue2.iid, relative_issue4.iid - ] - end - end - end - end - - context 'when sorting by label priority' do - let_it_be(:label1) { create(:label, project: sort_project, priority: 1) } - let_it_be(:label2) { create(:label, project: sort_project, priority: 5) } - let_it_be(:label3) { create(:label, project: sort_project, priority: 10) } - let_it_be(:label_issue1) { create(:issue, project: sort_project, labels: [label1]) } - let_it_be(:label_issue2) { create(:issue, project: sort_project, labels: [label2]) } - let_it_be(:label_issue3) { create(:issue, project: sort_project, labels: [label1, label3]) } - let_it_be(:label_issue4) { create(:issue, project: sort_project) } - - context 'when ascending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :LABEL_PRIORITY_ASC } - let(:first_param) { 2 } - let(:all_records) { [label_issue3.iid, label_issue1.iid, label_issue2.iid, label_issue4.iid] } - end - end - - context 'when descending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :LABEL_PRIORITY_DESC } - let(:first_param) { 2 } - let(:all_records) { [label_issue2.iid, label_issue3.iid, label_issue1.iid, label_issue4.iid] } - end - end - end - # rubocop:enable RSpec/MultipleMemoizedHelpers - - context 'when sorting by milestone due date' do - let_it_be(:early_milestone) { create(:milestone, project: sort_project, due_date: 10.days.from_now) } - let_it_be(:late_milestone) { create(:milestone, project: sort_project, due_date: 30.days.from_now) } - let_it_be(:milestone_issue1) { create(:issue, project: sort_project) } - let_it_be(:milestone_issue2) { create(:issue, project: sort_project, milestone: early_milestone) } - let_it_be(:milestone_issue3) { create(:issue, project: sort_project, milestone: late_milestone) } - - context 'when ascending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :MILESTONE_DUE_ASC } - let(:first_param) { 2 } - let(:all_records) { [milestone_issue2.iid, milestone_issue3.iid, milestone_issue1.iid] } - end - end - - context 'when descending' do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { :MILESTONE_DUE_DESC } - let(:first_param) { 2 } - let(:all_records) { [milestone_issue3.iid, milestone_issue2.iid, milestone_issue1.iid] } - end - end - end - end - - context 'when fetching alert management alert' do - let(:fields) do - <<~QUERY - nodes { - iid - alertManagementAlert { - title - } - alertManagementAlerts { - nodes { - title - } - } - } - QUERY - end - - # Alerts need to have developer permission and above - before do - project.add_developer(current_user) - end - - it 'avoids N+1 queries' do - control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - - create(:alert_management_alert, :with_incident, project: project) - - expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control) - end - - it 'returns the alert data' do - post_graphql(query, current_user: current_user) - - alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlert', 'title') } - expected_titles = issues.map { |issue| issue.alert_management_alert&.title } - - expect(alert_titles).to contain_exactly(*expected_titles) - end - - it 'returns the alerts data' do - post_graphql(query, current_user: current_user) - - alert_titles = issues_data.map { |issue| issue.dig('alertManagementAlerts', 'nodes') } - expected_titles = issues.map do |issue| - issue.alert_management_alerts.map { |alert| { 'title' => alert.title } } - end - - expect(alert_titles).to contain_exactly(*expected_titles) - end - end - - context 'when fetching customer_relations_contacts' do - let(:fields) do - <<~QUERY - nodes { - id - customerRelationsContacts { - nodes { - firstName - } - } - } - QUERY - end - - def clean_state_query - run_with_clean_state(query, context: { current_user: current_user }) - end - - it 'avoids N+1 queries' do - create(:issue_customer_relations_contact, :for_issue, issue: issue_a) - - control = ActiveRecord::QueryRecorder.new(skip_cached: false) { clean_state_query } - - create(:issue_customer_relations_contact, :for_issue, issue: issue_a) - - expect { clean_state_query }.not_to exceed_all_query_limit(control) - end - end - - context 'when fetching labels' do - let(:fields) do - <<~QUERY - nodes { - id - labels { - nodes { - id - } - } - } - QUERY - end - - before do - issues.each do |issue| - # create a label for each issue we have to properly test N+1 - label = create(:label, project: project) - issue.update!(labels: [label]) - end - end - - def response_label_ids(response_data) - response_data.map do |node| - node['labels']['nodes'].map { |u| u['id'] } - end.flatten - end - - def labels_as_global_ids(issues) - issues.map(&:labels).flatten.map(&:to_global_id).map(&:to_s) - end - - it 'avoids N+1 queries', :aggregate_failures do - control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - expect(issues_data.count).to eq(5) - expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(issues)) - - new_issues = issues + [create(:issue, project: project, labels: [create(:label, project: project)])] - - expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control) - # graphql_data is memoized (see spec/support/helpers/graphql_helpers.rb) - # so we have to parse the body ourselves the second time - issues_data = Gitlab::Json.parse(response.body)['data']['project']['issues']['nodes'] - expect(issues_data.count).to eq(6) - expect(response_label_ids(issues_data)).to match_array(labels_as_global_ids(new_issues)) - end - end - - context 'when fetching assignees' do - let(:fields) do - <<~QUERY - nodes { - id - assignees { - nodes { - id - } - } - } - QUERY - end - - before do - issues.each do |issue| - # create an assignee for each issue we have to properly test N+1 - assignee = create(:user) - issue.update!(assignees: [assignee]) - end - end - - def response_assignee_ids(response_data) - response_data.map do |node| - node['assignees']['nodes'].map { |node| node['id'] } - end.flatten - end - - def assignees_as_global_ids(issues) - issues.map(&:assignees).flatten.map(&:to_global_id).map(&:to_s) - end - - it 'avoids N+1 queries', :aggregate_failures do - control = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - expect(issues_data.count).to eq(5) - expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(issues)) - - new_issues = issues + [create(:issue, project: project, assignees: [create(:user)])] - - expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control) - # graphql_data is memoized (see spec/support/helpers/graphql_helpers.rb) - # so we have to parse the body ourselves the second time - issues_data = Gitlab::Json.parse(response.body)['data']['project']['issues']['nodes'] - expect(issues_data.count).to eq(6) - expect(response_assignee_ids(issues_data)).to match_array(assignees_as_global_ids(new_issues)) - end - end - - context 'when fetching escalation status' do - let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue_a) } - - let(:statuses) { issue_data.to_h { |issue| [issue['iid'], issue['escalationStatus']] } } - let(:fields) do - <<~QUERY - nodes { - id - escalationStatus - } - QUERY - end - - before do - issue_a.update!(issue_type: Issue.issue_types[:incident]) - end - - it 'returns the escalation status values' do - post_graphql(query, current_user: current_user) - - statuses = issues_data.map { |issue| issue['escalationStatus'] } - - expect(statuses).to contain_exactly(escalation_status.status_name.upcase.to_s, nil, nil, nil, nil) - end - - it 'avoids N+1 queries', :aggregate_failures do - base_count = ActiveRecord::QueryRecorder.new { run_with_clean_state(query, context: { current_user: current_user }) } - - new_incident = create(:incident, project: project) - create(:incident_management_issuable_escalation_status, issue: new_incident) - - expect { run_with_clean_state(query, context: { current_user: current_user }) }.not_to exceed_query_limit(base_count) - end - end - - describe 'N+1 query checks' do - let(:extra_iid_for_second_query) { issue_b.iid.to_s } - let(:search_params) { { iids: [issue_a.iid.to_s] } } - - def execute_query - query = graphql_query_for( - :project, - { full_path: project.full_path }, - query_graphql_field( - :issues, search_params, - query_graphql_field(:nodes, nil, requested_fields) - ) - ) - post_graphql(query, current_user: current_user) - end - - context 'when requesting `user_notes_count`' do - let(:requested_fields) { [:user_notes_count] } - - before do - create_list(:note_on_issue, 2, noteable: issue_a, project: project) - create(:note_on_issue, noteable: issue_b, project: project) - end - - include_examples 'N+1 query check' - end - - context 'when requesting `user_discussions_count`' do - let(:requested_fields) { [:user_discussions_count] } - - before do - create_list(:note_on_issue, 2, noteable: issue_a, project: project) - create(:note_on_issue, noteable: issue_b, project: project) - end - - include_examples 'N+1 query check' - end - - context 'when requesting `merge_requests_count`' do - let(:requested_fields) { [:merge_requests_count] } - - before do - create_list(:merge_requests_closing_issues, 2, issue: issue_a) - create_list(:merge_requests_closing_issues, 3, issue: issue_b) - end - - include_examples 'N+1 query check' - end - - context 'when requesting `timelogs`' do - let(:requested_fields) { 'timelogs { nodes { timeSpent } }' } - - before do - create_list(:issue_timelog, 2, issue: issue_a) - create(:issue_timelog, issue: issue_b) - end - - include_examples 'N+1 query check' - end - - context 'when requesting `closed_as_duplicate_of`' do - let(:requested_fields) { 'closedAsDuplicateOf { id }' } - let(:issue_a_dup) { create(:issue, project: project) } - let(:issue_b_dup) { create(:issue, project: project) } - - before do - issue_a.update!(duplicated_to_id: issue_a_dup) - issue_b.update!(duplicated_to_id: issue_a_dup) - end - - include_examples 'N+1 query check' - end - - context 'when award emoji votes' do - let(:requested_fields) { [:upvotes, :downvotes] } - - before do - create_list(:award_emoji, 2, name: 'thumbsup', awardable: issue_a) - create_list(:award_emoji, 2, name: 'thumbsdown', awardable: issue_b) - end - - include_examples 'N+1 query check' - end - - context 'when requesting participants' do - let_it_be(:issue_c) { create(:issue, project: project) } - - let(:search_params) { { iids: [issue_a.iid.to_s, issue_c.iid.to_s] } } - let(:requested_fields) { 'participants { nodes { name } }' } - - before do - create(:award_emoji, :upvote, awardable: issue_a) - create(:award_emoji, :upvote, awardable: issue_b) - create(:award_emoji, :upvote, awardable: issue_c) - - note_with_emoji_a = create(:note_on_issue, noteable: issue_a, project: project) - note_with_emoji_b = create(:note_on_issue, noteable: issue_b, project: project) - note_with_emoji_c = create(:note_on_issue, noteable: issue_c, project: project) - - create(:award_emoji, :upvote, awardable: note_with_emoji_a) - create(:award_emoji, :upvote, awardable: note_with_emoji_b) - create(:award_emoji, :upvote, awardable: note_with_emoji_c) - end - - # Executes 3 extra queries to fetch participant_attrs - include_examples 'N+1 query check', threshold: 3 - end - - context 'when requesting labels' do - let(:requested_fields) { ['labels { nodes { id } }'] } - - before do - project_labels = create_list(:label, 2, project: project) - group_labels = create_list(:group_label, 2, group: group) - - issue_a.update!(labels: [project_labels.first, group_labels.first].flatten) - issue_b.update!(labels: [project_labels, group_labels].flatten) - end - - include_examples 'N+1 query check', skip_cached: false - end - end - - def issue_ids - graphql_dig_at(issues_data, :id) end def query(params = issue_filter_params) diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb index 202220f4bf6..821357b6988 100644 --- a/spec/requests/api/graphql/project/jira_import_spec.rb +++ b/spec/requests/api/graphql/project/jira_import_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query Jira import data' do +RSpec.describe 'query Jira import data', feature_category: :integrations do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/jira_projects_spec.rb b/spec/requests/api/graphql/project/jira_projects_spec.rb index 410d5b21505..3cd689deda5 100644 --- a/spec/requests/api/graphql/project/jira_projects_spec.rb +++ b/spec/requests/api/graphql/project/jira_projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query Jira projects' do +RSpec.describe 'query Jira projects', feature_category: :integrations do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/jira_service_spec.rb b/spec/requests/api/graphql/project/jira_service_spec.rb index d6abe94b873..23f32d2c2d2 100644 --- a/spec/requests/api/graphql/project/jira_service_spec.rb +++ b/spec/requests/api/graphql/project/jira_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query Jira service' do +RSpec.describe 'query Jira service', feature_category: :integrations do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/project/job_spec.rb b/spec/requests/api/graphql/project/job_spec.rb index 6edd4cf753f..ba1c8a1f616 100644 --- a/spec/requests/api/graphql/project/job_spec.rb +++ b/spec/requests/api/graphql/project/job_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project.job' do +RSpec.describe 'Query.project.job', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb index 7d0eb203d60..d05d4a2f4b6 100644 --- a/spec/requests/api/graphql/project/jobs_spec.rb +++ b/spec/requests/api/graphql/project/jobs_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'Query.project.jobs' do +RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/labels_query_spec.rb b/spec/requests/api/graphql/project/labels_query_spec.rb index eeaaaaee575..1930a22ad30 100644 --- a/spec/requests/api/graphql/project/labels_query_spec.rb +++ b/spec/requests/api/graphql/project/labels_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project label information' do +RSpec.describe 'getting project label information', feature_category: :team_planning do include GraphqlHelpers let_it_be(:project) { create(:project, :public) } diff --git a/spec/requests/api/graphql/project/languages_spec.rb b/spec/requests/api/graphql/project/languages_spec.rb index 6ef500cde41..88a196c3ff4 100644 --- a/spec/requests/api/graphql/project/languages_spec.rb +++ b/spec/requests/api/graphql/project/languages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Project.languages' do +RSpec.describe 'Project.languages', feature_category: :internationalization do include GraphqlHelpers let_it_be(:user) { create(:user) } diff --git a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb index b1ecb32b365..36e148468bc 100644 --- a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb +++ b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting notes for a merge request' do +RSpec.describe 'getting notes for a merge request', feature_category: :code_review do include GraphqlHelpers let_it_be(:noteable) { create(:merge_request) } diff --git a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb index 4dc272b5c2e..fb7e46cff8e 100644 --- a/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb +++ b/spec/requests/api/graphql/project/merge_request/pipelines_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project.mergeRequests.pipelines' do +RSpec.describe 'Query.project.mergeRequests.pipelines', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :public, :repository) } diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index 6a59df81405..b7aafdf305a 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting merge request information nested in a project' do +RSpec.describe 'getting merge request information nested in a project', feature_category: :code_review do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index 2895737ae6f..b3b4c8fe0d5 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting merge request listings nested in a project' do +RSpec.describe 'getting merge request listings nested in a project', feature_category: :code_review do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb index a577c367fe5..3b31da77a75 100644 --- a/spec/requests/api/graphql/project/milestones_spec.rb +++ b/spec/requests/api/graphql/project/milestones_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting milestone listings nested in a project' do +RSpec.describe 'getting milestone listings nested in a project', feature_category: :team_planning do include GraphqlHelpers let_it_be(:today) { Time.now.utc.to_date } diff --git a/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb index 33e1dbcba27..a7d5cc79f1a 100644 --- a/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb +++ b/spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting the packages cleanup policy linked to a project' do +RSpec.describe 'getting the packages cleanup policy linked to a project', feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include GraphqlHelpers diff --git a/spec/requests/api/graphql/project/packages_spec.rb b/spec/requests/api/graphql/project/packages_spec.rb index d9ee997eb02..3413b80e8b4 100644 --- a/spec/requests/api/graphql/project/packages_spec.rb +++ b/spec/requests/api/graphql/project/packages_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a package list for a project' do +RSpec.describe 'getting a package list for a project', feature_category: :package_registry do include GraphqlHelpers let_it_be(:resource) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index 41915d3cdee..0eeb382510e 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting pipeline information nested in a project' do +RSpec.describe 'getting pipeline information nested in a project', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project, :repository, :public) } diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb index 97a79ab3b0e..1f1d8027592 100644 --- a/spec/requests/api/graphql/project/project_members_spec.rb +++ b/spec/requests/api/graphql/project/project_members_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project members information' do +RSpec.describe 'getting project members information', feature_category: :projects do include GraphqlHelpers let_it_be(:parent_group) { create(:group, :public) } diff --git a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb index 39a68d98d84..a13e96eb9d3 100644 --- a/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb +++ b/spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'rendering project pipeline statistics' do +RSpec.describe 'rendering project pipeline statistics', feature_category: :continuous_integration do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/project_statistics_spec.rb b/spec/requests/api/graphql/project/project_statistics_spec.rb index b57c594c64f..d078659b954 100644 --- a/spec/requests/api/graphql/project/project_statistics_spec.rb +++ b/spec/requests/api/graphql/project/project_statistics_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'rendering project statistics' do +RSpec.describe 'rendering project statistics', feature_category: :project_statistics do include GraphqlHelpers let(:project) { create(:project) } diff --git a/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb b/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb index b3daf86c4af..ae459fd26fb 100644 --- a/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb +++ b/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project recent issue boards' do +RSpec.describe 'getting project recent issue boards', feature_category: :team_planning do include GraphqlHelpers it_behaves_like 'querying a GraphQL type recent boards' do diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index c4899dbb71e..477388585ca 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).release(tagName)' do +RSpec.describe 'Query.project(fullPath).release(tagName)', feature_category: :release_orchestration do include GraphqlHelpers include Presentable diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb index c28a6fa7666..aa454349fcf 100644 --- a/spec/requests/api/graphql/project/releases_spec.rb +++ b/spec/requests/api/graphql/project/releases_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.project(fullPath).releases()' do +RSpec.describe 'Query.project(fullPath).releases()', feature_category: :release_orchestration do include GraphqlHelpers let_it_be(:stranger) { create(:user) } diff --git a/spec/requests/api/graphql/project/repository/blobs_spec.rb b/spec/requests/api/graphql/project/repository/blobs_spec.rb index ba87f1100f2..a4ee0910d30 100644 --- a/spec/requests/api/graphql/project/repository/blobs_spec.rb +++ b/spec/requests/api/graphql/project/repository/blobs_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting blobs in a project repository' do +RSpec.describe 'getting blobs in a project repository', feature_category: :source_code_management do include GraphqlHelpers let(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/repository_spec.rb b/spec/requests/api/graphql/project/repository_spec.rb index b00f64c3db6..9f4d69c7b17 100644 --- a/spec/requests/api/graphql/project/repository_spec.rb +++ b/spec/requests/api/graphql/project/repository_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting a repository in a project' do +RSpec.describe 'getting a repository in a project', feature_category: :source_code_management do include GraphqlHelpers let(:project) { create(:project, :repository) } diff --git a/spec/requests/api/graphql/project/runners_spec.rb b/spec/requests/api/graphql/project/runners_spec.rb new file mode 100644 index 00000000000..7304de7bec6 --- /dev/null +++ b/spec/requests/api/graphql/project/runners_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Project.runners', feature_category: :runner do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:group) { create(:group, :public) } + let_it_be(:project) { create(:project, :public, group: group) } + let_it_be(:instance_runner) { create(:ci_runner, :instance) } + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) } + let_it_be(:other_project) { create(:project, :repository, :public) } + let_it_be(:other_project_runner) { create(:ci_runner, :project, projects: [other_project]) } + + let_it_be(:query) do + %( + query { + project(fullPath: "#{project.full_path}") { + runners { + nodes { + id + } + } + } + } + ) + end + + context 'when the user is a project admin' do + before do + project.add_maintainer(user) + end + + let(:expected_ids) { [project_runner, group_runner, instance_runner].map { |g| g.to_global_id.to_s } } + + it 'returns all runners available to project' do + post_graphql(query, current_user: user) + + expect(graphql_data_at(:project, :runners, :nodes).pluck('id')).to match_array(expected_ids) + end + end + + context 'when the user is a project developer' do + before do + project.add_developer(user) + end + + it 'returns no runners' do + post_graphql(query, current_user: user) + + expect(graphql_data_at(:project, :runners, :nodes)).to be_empty + end + end + + context 'when on_demand_scans_runner_tags feature flag is disabled' do + before do + stub_feature_flags(on_demand_scans_runner_tags: false) + end + + it 'returns no runners' do + post_graphql(query, current_user: user) + + expect(graphql_data_at(:project, :runners, :nodes)).to be_empty + end + end +end diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb index 5e207ec0963..1889e7a1064 100644 --- a/spec/requests/api/graphql/project/terraform/state_spec.rb +++ b/spec/requests/api/graphql/project/terraform/state_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query a single terraform state' do +RSpec.describe 'query a single terraform state', feature_category: :infrastructure_as_code do include GraphqlHelpers include ::API::Helpers::RelatedResourcesHelpers diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb index cc3660bcc6b..25fc07ef509 100644 --- a/spec/requests/api/graphql/project/terraform/states_spec.rb +++ b/spec/requests/api/graphql/project/terraform/states_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'query terraform states' do +RSpec.describe 'query terraform states', feature_category: :infrastructure_as_code do include GraphqlHelpers include ::API::Helpers::RelatedResourcesHelpers diff --git a/spec/requests/api/graphql/project/tree/tree_spec.rb b/spec/requests/api/graphql/project/tree/tree_spec.rb index e63e0d3dd04..77b72bf39a1 100644 --- a/spec/requests/api/graphql/project/tree/tree_spec.rb +++ b/spec/requests/api/graphql/project/tree/tree_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'spec_helper' -RSpec.describe 'getting a tree in a project' do +RSpec.describe 'getting a tree in a project', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } @@ -166,6 +166,54 @@ RSpec.describe 'getting a tree in a project' do end end + context 'when the ref points to a SSH-signed commit' do + let_it_be(:ref) { 'ssh-signed-commit' } + let_it_be(:commit) { project.commit(ref) } + let_it_be(:current_user) { create(:user, email: commit.committer_email).tap { |user| project.add_owner(user) } } + + let(:fields) do + <<~QUERY + tree(path:"#{path}", ref:"#{ref}") { + lastCommit { + signature { + ... on SshSignature { + #{all_graphql_fields_for('SshSignature'.classify, max_depth: 2)} + } + } + } + } + QUERY + end + + let_it_be(:key) do + create(:key, user: current_user, key: extract_public_key_from_commit(commit), expires_at: 2.days.from_now) + end + + def extract_public_key_from_commit(commit) + ssh_commit = Gitlab::Ssh::Commit.new(commit) + signature_data = ::SSHData::Signature.parse_pem(ssh_commit.signature_text) + signature_data.public_key.openssh + end + + before do + post_graphql(query, current_user: current_user) + end + + it 'returns the expected signature data' do + signature = graphql_data['project']['repository']['tree']['lastCommit']['signature'] + + expect(signature['commitSha']).to eq(commit.id) + expect(signature['verificationStatus']).to eq('VERIFIED') + expect(signature['project']['id']).to eq("gid://gitlab/Project/#{project.id}") + expect(signature['user']['id']).to eq("gid://gitlab/User/#{current_user.id}") + expect(signature['key']['id']).to eq("gid://gitlab/Key/#{key.id}") + expect(signature['key']['title']).to eq(key.title) + expect(signature['key']['createdAt']).to be_present + expect(signature['key']['expiresAt']).to be_present + expect(signature['key']['key']).to match(key.key) + end + end + context 'when current user is nil' do it 'returns empty project' do post_graphql(query, current_user: nil) diff --git a/spec/requests/api/graphql/project/work_item_types_spec.rb b/spec/requests/api/graphql/project/work_item_types_spec.rb index 3d30baab816..c31a260c4b8 100644 --- a/spec/requests/api/graphql/project/work_item_types_spec.rb +++ b/spec/requests/api/graphql/project/work_item_types_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a list of work item types for a project' do +RSpec.describe 'getting a list of work item types for a project', feature_category: :team_planning do include GraphqlHelpers let_it_be(:developer) { create(:user) } diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb index 6d20799c9ec..a59da706a8a 100644 --- a/spec/requests/api/graphql/project/work_items_spec.rb +++ b/spec/requests/api/graphql/project/work_items_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting a work item list for a project' do +RSpec.describe 'getting a work item list for a project', feature_category: :team_planning do include GraphqlHelpers let_it_be(:group) { create(:group) } @@ -93,6 +93,11 @@ RSpec.describe 'getting a work item list for a project' do } ... on WorkItemWidgetHierarchy { parent { id } + children { + nodes { + id + } + } } ... on WorkItemWidgetLabels { labels { nodes { id } } @@ -112,6 +117,57 @@ RSpec.describe 'getting a work item list for a project' do end end + context 'when querying WorkItemWidgetHierarchy' do + let_it_be(:children) { create_list(:work_item, 3, :task, project: project) } + let_it_be(:child_link1) { create(:parent_link, work_item_parent: item1, work_item: children[0]) } + + let(:fields) do + <<~GRAPHQL + nodes { + widgets { + type + ... on WorkItemWidgetHierarchy { + hasChildren + parent { id } + children { nodes { id } } + } + } + } + GRAPHQL + end + + it 'executes limited number of N+1 queries' do + post_graphql(query, current_user: current_user) # warm-up + + control = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: current_user) + end + + parent_work_items = create_list(:work_item, 2, project: project) + create(:parent_link, work_item_parent: parent_work_items[0], work_item: children[1]) + create(:parent_link, work_item_parent: parent_work_items[1], work_item: children[2]) + + # There are 2 extra queries for fetching the children field + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/363569 + expect { post_graphql(query, current_user: current_user) } + .not_to exceed_query_limit(control).with_threshold(2) + end + + it 'avoids N+1 queries when children are added to a work item' do + post_graphql(query, current_user: current_user) # warm-up + + control = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: current_user) + end + + create(:parent_link, work_item_parent: item1, work_item: children[1]) + create(:parent_link, work_item_parent: item1, work_item: children[2]) + + expect { post_graphql(query, current_user: current_user) } + .not_to exceed_query_limit(control) + end + end + it_behaves_like 'a working graphql query' do before do post_graphql(query, current_user: current_user) @@ -188,6 +244,60 @@ RSpec.describe 'getting a work item list for a project' do end end + describe 'fetching work item notes widget' do + let(:item_filter_params) { { iid: item2.iid.to_s } } + let(:fields) do + <<~GRAPHQL + edges { + node { + widgets { + type + ... on WorkItemWidgetNotes { + system: discussions(filter: ONLY_ACTIVITY, first: 10) { nodes { id notes { nodes { id system internal body } } } }, + comments: discussions(filter: ONLY_COMMENTS, first: 10) { nodes { id notes { nodes { id system internal body } } } }, + all_notes: discussions(filter: ALL_NOTES, first: 10) { nodes { id notes { nodes { id system internal body } } } } + } + } + } + } + GRAPHQL + end + + before do + create_notes(item1, "some note1") + create_notes(item2, "some note2") + end + + shared_examples 'fetches work item notes' do |user_comments_count:, system_notes_count:| + it "fetches notes" do + post_graphql(query, current_user: current_user) + + all_widgets = graphql_dig_at(items_data, :node, :widgets) + notes_widget = all_widgets.find { |x| x["type"] == "NOTES" } + + all_notes = graphql_dig_at(notes_widget["all_notes"], :nodes) + system_notes = graphql_dig_at(notes_widget["system"], :nodes) + comments = graphql_dig_at(notes_widget["comments"], :nodes) + + expect(comments.count).to eq(user_comments_count) + expect(system_notes.count).to eq(system_notes_count) + expect(all_notes.count).to eq(user_comments_count + system_notes_count) + end + end + + context 'when user has permission to view internal notes' do + before do + project.add_developer(current_user) + end + + it_behaves_like 'fetches work item notes', user_comments_count: 2, system_notes_count: 5 + end + + context 'when user cannot view internal notes' do + it_behaves_like 'fetches work item notes', user_comments_count: 1, system_notes_count: 5 + end + end + def item_ids graphql_dig_at(items_data, :node, :id) end @@ -199,4 +309,26 @@ RSpec.describe 'getting a work item list for a project' do query_graphql_field('workItems', params, fields) ) end + + def create_notes(work_item, note_body) + create(:note, system: true, project: work_item.project, noteable: work_item) + + disc_start = create(:discussion_note_on_issue, noteable: work_item, project: work_item.project, note: note_body) + create(:note, + discussion_id: disc_start.discussion_id, noteable: work_item, + project: work_item.project, note: "reply on #{note_body}") + + create(:resource_label_event, user: current_user, issue: work_item, label: label1, action: 'add') + create(:resource_label_event, user: current_user, issue: work_item, label: label1, action: 'remove') + + create(:resource_milestone_event, issue: work_item, milestone: milestone1, action: 'add') + create(:resource_milestone_event, issue: work_item, milestone: milestone1, action: 'remove') + + # confidential notes are currently available only on issues and epics + conf_disc_start = create(:discussion_note_on_issue, :confidential, + noteable: work_item, project: work_item.project, note: "confidential #{note_body}") + create(:note, :confidential, + discussion_id: conf_disc_start.discussion_id, noteable: work_item, + project: work_item.project, note: "reply on confidential #{note_body}") + end end diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index d1b990629a1..281a08e6548 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting project information' do +RSpec.describe 'getting project information', feature_category: :projects do include GraphqlHelpers let_it_be(:group) { create(:group) } diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb index 359c599cd3a..2b9d66ec744 100644 --- a/spec/requests/api/graphql/query_spec.rb +++ b/spec/requests/api/graphql/query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query' do +RSpec.describe 'Query', feature_category: :not_owned do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/read_only_spec.rb b/spec/requests/api/graphql/read_only_spec.rb index d2a45603886..aec8d3151c8 100644 --- a/spec/requests/api/graphql/read_only_spec.rb +++ b/spec/requests/api/graphql/read_only_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Requests on a read-only node' do +RSpec.describe 'Requests on a read-only node', feature_category: :database do context 'when db is read-only' do before do allow(Gitlab::Database).to receive(:read_only?) { true } diff --git a/spec/requests/api/graphql/snippets_spec.rb b/spec/requests/api/graphql/snippets_spec.rb index 9edd805678a..3cd95b0841c 100644 --- a/spec/requests/api/graphql/snippets_spec.rb +++ b/spec/requests/api/graphql/snippets_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'snippets' do +RSpec.describe 'snippets', feature_category: :source_code_management do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/tasks/task_completion_status_spec.rb b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb index 5f4d2aec718..ea89487c176 100644 --- a/spec/requests/api/graphql/tasks/task_completion_status_spec.rb +++ b/spec/requests/api/graphql/tasks/task_completion_status_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting task completion status information' do +RSpec.describe 'getting task completion status information', feature_category: :team_planning do include GraphqlHelpers description_0_done = '- [ ] task 1\n- [ ] task 2' diff --git a/spec/requests/api/graphql/terraform/state/delete_spec.rb b/spec/requests/api/graphql/terraform/state/delete_spec.rb index ba0619ea611..f4af402492c 100644 --- a/spec/requests/api/graphql/terraform/state/delete_spec.rb +++ b/spec/requests/api/graphql/terraform/state/delete_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'delete a terraform state' do +RSpec.describe 'delete a terraform state', feature_category: :infrastructure_as_code do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/terraform/state/lock_spec.rb b/spec/requests/api/graphql/terraform/state/lock_spec.rb index e4d3b6336ab..4219f4f4651 100644 --- a/spec/requests/api/graphql/terraform/state/lock_spec.rb +++ b/spec/requests/api/graphql/terraform/state/lock_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'lock a terraform state' do +RSpec.describe 'lock a terraform state', feature_category: :infrastructure_as_code do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/terraform/state/unlock_spec.rb b/spec/requests/api/graphql/terraform/state/unlock_spec.rb index e90730f2d8f..84ccc09711d 100644 --- a/spec/requests/api/graphql/terraform/state/unlock_spec.rb +++ b/spec/requests/api/graphql/terraform/state/unlock_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'unlock a terraform state' do +RSpec.describe 'unlock a terraform state', feature_category: :infrastructure_as_code do include GraphqlHelpers let_it_be(:project) { create(:project) } diff --git a/spec/requests/api/graphql/todo_query_spec.rb b/spec/requests/api/graphql/todo_query_spec.rb index 7fe19448083..b8ce065dbbb 100644 --- a/spec/requests/api/graphql/todo_query_spec.rb +++ b/spec/requests/api/graphql/todo_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Todo Query' do +RSpec.describe 'Todo Query', feature_category: :team_planning do include GraphqlHelpers let_it_be(:current_user) { nil } diff --git a/spec/requests/api/graphql/usage_trends_measurements_spec.rb b/spec/requests/api/graphql/usage_trends_measurements_spec.rb index 78a4321f522..68b97fbb130 100644 --- a/spec/requests/api/graphql/usage_trends_measurements_spec.rb +++ b/spec/requests/api/graphql/usage_trends_measurements_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'UsageTrendsMeasurements' do +RSpec.describe 'UsageTrendsMeasurements', feature_category: :devops_reports do include GraphqlHelpers let(:current_user) { create(:user, :admin) } diff --git a/spec/requests/api/graphql/user/group_member_query_spec.rb b/spec/requests/api/graphql/user/group_member_query_spec.rb index e47cef8cc37..d09cb319877 100644 --- a/spec/requests/api/graphql/user/group_member_query_spec.rb +++ b/spec/requests/api/graphql/user/group_member_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'GroupMember' do +RSpec.describe 'GroupMember', feature_category: :subgroups do include GraphqlHelpers let_it_be(:member) { create(:group_member, :developer) } diff --git a/spec/requests/api/graphql/user/project_member_query_spec.rb b/spec/requests/api/graphql/user/project_member_query_spec.rb index 01827e94d5d..1baa7815793 100644 --- a/spec/requests/api/graphql/user/project_member_query_spec.rb +++ b/spec/requests/api/graphql/user/project_member_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ProjectMember' do +RSpec.describe 'ProjectMember', feature_category: :subgroups do include GraphqlHelpers let_it_be(:member) { create(:project_member, :developer) } diff --git a/spec/requests/api/graphql/user/starred_projects_query_spec.rb b/spec/requests/api/graphql/user/starred_projects_query_spec.rb index 75a17ed34c4..7d4284300d8 100644 --- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb +++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Getting starredProjects of the user' do +RSpec.describe 'Getting starredProjects of the user', feature_category: :projects do include GraphqlHelpers let(:query) do diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb index 8f286180617..ca319ed1b2e 100644 --- a/spec/requests/api/graphql/user_query_spec.rb +++ b/spec/requests/api/graphql/user_query_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'getting user information' do +RSpec.describe 'getting user information', feature_category: :user_management do include GraphqlHelpers let(:query) do diff --git a/spec/requests/api/graphql/user_spec.rb b/spec/requests/api/graphql/user_spec.rb index a3b2b750bc3..2e1e4971767 100644 --- a/spec/requests/api/graphql/user_spec.rb +++ b/spec/requests/api/graphql/user_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'User' do +RSpec.describe 'User', feature_category: :users do include GraphqlHelpers let_it_be(:current_user) { create(:user) } diff --git a/spec/requests/api/graphql/users_spec.rb b/spec/requests/api/graphql/users_spec.rb index 79ee3c2cb57..83c360fdaf8 100644 --- a/spec/requests/api/graphql/users_spec.rb +++ b/spec/requests/api/graphql/users_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Users' do +RSpec.describe 'Users', feature_category: :user_management do include GraphqlHelpers let_it_be(:user0) { create(:user, created_at: 1.day.ago) } diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb index a55de6adfb2..df7dbaea420 100644 --- a/spec/requests/api/graphql/work_item_spec.rb +++ b/spec/requests/api/graphql/work_item_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Query.work_item(id)' do +RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do include GraphqlHelpers let_it_be(:developer) { create(:user) } @@ -116,6 +116,7 @@ RSpec.describe 'Query.work_item(id)' do id } } + hasChildren } } GRAPHQL @@ -132,7 +133,8 @@ RSpec.describe 'Query.work_item(id)' do [ hash_including('id' => child_link1.work_item.to_gid.to_s), hash_including('id' => child_link2.work_item.to_gid.to_s) - ]) } + ]) }, + 'hasChildren' => true ) ) ) @@ -165,7 +167,8 @@ RSpec.describe 'Query.work_item(id)' do 'children' => { 'nodes' => match_array( [ hash_including('id' => child_link1.work_item.to_gid.to_s) - ]) } + ]) }, + 'hasChildren' => true ) ) ) @@ -183,7 +186,8 @@ RSpec.describe 'Query.work_item(id)' do hash_including( 'type' => 'HIERARCHY', 'parent' => hash_including('id' => parent_link.work_item_parent.to_gid.to_s), - 'children' => { 'nodes' => match_array([]) } + 'children' => { 'nodes' => match_array([]) }, + 'hasChildren' => false ) ) ) |