diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-18 09:45:46 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-18 09:45:46 +0000 |
commit | a7b3560714b4d9cc4ab32dffcd1f74a284b93580 (patch) | |
tree | 7452bd5c3545c2fa67a28aa013835fb4fa071baf /spec/requests/api/graphql | |
parent | ee9173579ae56a3dbfe5afe9f9410c65bb327ca7 (diff) | |
download | gitlab-ce-a7b3560714b4d9cc4ab32dffcd1f74a284b93580.tar.gz |
Add latest changes from gitlab-org/gitlab@14-8-stable-eev14.8.0-rc42
Diffstat (limited to 'spec/requests/api/graphql')
33 files changed, 503 insertions, 49 deletions
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 578a71a7272..c19defa37e8 100644 --- a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb +++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'Getting Ci Cd Setting' do include GraphqlHelpers let_it_be_with_reload(:project) { create(:project, :repository) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let(:fields) do <<~QUERY diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb index 755585f8e0e..62b15a8396c 100644 --- a/spec/requests/api/graphql/ci/config_spec.rb +++ b/spec/requests/api/graphql/ci/config_spec.rb @@ -225,7 +225,7 @@ RSpec.describe 'Query.ciConfig' do context 'when using deprecated keywords' do let_it_be(:content) do YAML.dump( - rspec: { script: 'ls' }, + rspec: { script: 'ls', type: 'test' }, types: ['test'] ) end @@ -233,7 +233,10 @@ RSpec.describe 'Query.ciConfig' do it 'returns a warning' do post_graphql_query - expect(graphql_data['ciConfig']['warnings']).to include('root `types` is deprecated in 9.0 and will be removed in 15.0.') + expect(graphql_data['ciConfig']['warnings']).to include( + 'root `types` is deprecated in 9.0 and will be removed in 15.0.', + 'jobs:rspec `type` is deprecated in 9.0 and will be removed in 15.0.' + ) end end diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 8c919b48849..fa16b9e1ddd 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -25,6 +25,8 @@ RSpec.describe 'Query.runner(id)' do access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :shell) end + let_it_be(:active_project_runner) { create(:ci_runner, :project) } + def get_runner(id) case id when :active_instance_runner @@ -33,6 +35,8 @@ RSpec.describe 'Query.runner(id)' do inactive_instance_runner when :active_group_runner active_group_runner + when :active_project_runner + active_project_runner end end @@ -55,7 +59,7 @@ RSpec.describe 'Query.runner(id)' do runner = get_runner(runner_id) expect(runner_data).to match a_hash_including( - 'id' => "gid://gitlab/Ci::Runner/#{runner.id}", + 'id' => runner.to_global_id.to_s, 'description' => runner.description, 'createdAt' => runner.created_at&.iso8601, 'contactedAt' => runner.contacted_at&.iso8601, @@ -64,6 +68,7 @@ RSpec.describe 'Query.runner(id)' do '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, @@ -72,6 +77,7 @@ RSpec.describe 'Query.runner(id)' do 'runnerType' => runner.instance_type? ? 'INSTANCE_TYPE' : 'PROJECT_TYPE', 'executorName' => runner.executor_type&.dasherize, 'jobCount' => 0, + 'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything), 'projectCount' => nil, 'adminUrl' => "http://localhost/admin/runners/#{runner.id}", 'userPermissions' => { @@ -103,7 +109,7 @@ RSpec.describe 'Query.runner(id)' do runner = get_runner(runner_id) expect(runner_data).to match a_hash_including( - 'id' => "gid://gitlab/Ci::Runner/#{runner.id}", + 'id' => runner.to_global_id.to_s, 'adminUrl' => nil ) expect(runner_data['tagList']).to match_array runner.tag_list @@ -179,7 +185,7 @@ RSpec.describe 'Query.runner(id)' do runner_data = graphql_data_at(:runner) expect(runner_data).to match a_hash_including( - 'id' => "gid://gitlab/Ci::Runner/#{project_runner.id}", + 'id' => project_runner.to_global_id.to_s, 'locked' => is_locked ) end @@ -216,13 +222,36 @@ RSpec.describe 'Query.runner(id)' do a_hash_including( 'webUrl' => "http://localhost/groups/#{group.full_path}/-/runners/#{active_group_runner.id}", 'node' => { - 'id' => "gid://gitlab/Ci::Runner/#{active_group_runner.id}" + 'id' => active_group_runner.to_global_id.to_s } ) ] end end + describe 'for group runner request' do + let(:query) do + %( + query { + runner(id: "#{active_group_runner.to_global_id}") { + groups { + nodes { + id + } + } + } + } + ) + end + + 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 }] + end + end + describe 'for runner with status' do let_it_be(:stale_runner) { create(:ci_runner, description: 'Stale runner 1', created_at: 3.months.ago) } let_it_be(:never_contacted_instance_runner) { create(:ci_runner, description: 'Missing runner 1', created_at: 1.month.ago, contacted_at: nil) } @@ -279,21 +308,51 @@ RSpec.describe 'Query.runner(id)' do let!(:job) { create(:ci_build, runner: project_runner1) } - context 'requesting project and job counts' do + context 'requesting projects and counts for projects and jobs' do + let(:jobs_fragment) do + %( + jobs { + count + nodes { + id + status + } + } + ) + end + let(:query) do %( query { projectRunner1: runner(id: "#{project_runner1.to_global_id}") { projectCount jobCount + #{jobs_fragment} + projects { + nodes { + id + } + } } projectRunner2: runner(id: "#{project_runner2.to_global_id}") { projectCount jobCount + #{jobs_fragment} + projects { + nodes { + id + } + } } activeInstanceRunner: runner(id: "#{active_instance_runner.to_global_id}") { projectCount jobCount + #{jobs_fragment} + projects { + nodes { + id + } + } } } ) @@ -312,13 +371,29 @@ RSpec.describe 'Query.runner(id)' do expect(runner1_data).to match a_hash_including( 'jobCount' => 1, - 'projectCount' => 2) + 'jobs' => a_hash_including( + "count" => 1, + "nodes" => [{ "id" => job.to_global_id.to_s, "status" => job.status.upcase }] + ), + 'projectCount' => 2, + 'projects' => { + 'nodes' => [ + { 'id' => project1.to_global_id.to_s }, + { 'id' => project2.to_global_id.to_s } + ] + }) expect(runner2_data).to match a_hash_including( 'jobCount' => 0, - 'projectCount' => 0) + 'jobs' => nil, # returning jobs not allowed for more than 1 runner (see RunnerJobsResolver) + 'projectCount' => 0, + 'projects' => { + 'nodes' => [] + }) expect(runner3_data).to match a_hash_including( 'jobCount' => 0, - 'projectCount' => nil) + 'jobs' => nil, # returning jobs not allowed for more than 1 runner (see RunnerJobsResolver) + 'projectCount' => nil, + 'projects' => nil) end end end @@ -326,7 +401,17 @@ RSpec.describe 'Query.runner(id)' do describe 'by regular user' do let(:user) { create(:user) } - it_behaves_like 'retrieval by unauthorized user', :active_instance_runner + context 'on instance runner' do + it_behaves_like 'retrieval by unauthorized user', :active_instance_runner + end + + context 'on group runner' do + it_behaves_like 'retrieval by unauthorized user', :active_group_runner + end + + context 'on project runner' do + it_behaves_like 'retrieval by unauthorized user', :active_project_runner + end end describe 'by non-admin user' do diff --git a/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb b/spec/requests/api/graphql/container_repository/container_repository_details_spec.rb index 802ab847b3d..35a70a180a2 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 @@ -17,7 +17,7 @@ RSpec.describe 'container repository details' do ) end - let(:user) { project.owner } + let(:user) { project.first_owner } let(:variables) { {} } let(:tags) { %w[latest tag1 tag2 tag3 tag4 tag5] } let(:container_repository_global_id) { container_repository.to_global_id.to_s } diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb index 8bbeae97f57..e80f5e0e0ff 100644 --- a/spec/requests/api/graphql/gitlab_schema_spec.rb +++ b/spec/requests/api/graphql/gitlab_schema_spec.rb @@ -166,7 +166,7 @@ RSpec.describe 'GitlabSchema configurations' do end context 'authentication' do - let(:current_user) { project.owner } + let(:current_user) { project.first_owner } it 'authenticates all queries' do subject @@ -216,7 +216,7 @@ RSpec.describe 'GitlabSchema configurations' do context "global id's" do it 'uses GlobalID to expose ids' do post_graphql(graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id)), - current_user: project.owner) + current_user: project.first_owner) parsed_id = GlobalID.parse(graphql_data['project']['id']) 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 new file mode 100644 index 00000000000..4914beec870 --- /dev/null +++ b/spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting group recent issue boards' do + include GraphqlHelpers + + it_behaves_like 'querying a GraphQL type recent boards' do + let_it_be(:user) { create(:user) } + let_it_be(:parent) { create(:group, :public) } + let_it_be(:board) { create(:board, resource_parent: parent, name: 'test group board') } + let(:board_type) { 'group' } + end +end diff --git a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb index 05f6804a208..30e7f196542 100644 --- a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb @@ -45,7 +45,7 @@ RSpec.describe 'CiCdSettingsUpdate' do end context 'when authorized' do - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } it 'updates ci cd settings' do post_graphql_mutation(mutation, current_user: 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 b53a7ddde32..5269c60b50a 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 @@ -49,7 +49,7 @@ RSpec.describe 'CiJobTokenScopeAddProject' do end context 'when authorized' do - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } before do target_project.add_developer(current_user) 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 f1f42b00ada..b62291d1ebd 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 @@ -55,7 +55,7 @@ RSpec.describe 'CiJobTokenScopeRemoveProject' do end context 'when authorized' do - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } before do target_project.add_guest(current_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 08959d354e2..37656ab4eea 100644 --- a/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'PipelineDestroy' do include GraphqlHelpers let_it_be(:project) { create(:project) } - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project, user: user) } let(:mutation) 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 322706be119..12368e7e9c5 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 @@ -71,7 +71,7 @@ RSpec.describe 'RunnersRegistrationTokenReset' do end include_context 'when authorized', 'project' do - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } def get_token project.reload.runners_token diff --git a/spec/requests/api/graphql/mutations/issues/create_spec.rb b/spec/requests/api/graphql/mutations/issues/create_spec.rb index 6baed352b37..3d81b456c9c 100644 --- a/spec/requests/api/graphql/mutations/issues/create_spec.rb +++ b/spec/requests/api/graphql/mutations/issues/create_spec.rb @@ -52,5 +52,22 @@ RSpec.describe 'Create an issue' do it_behaves_like 'has spam protection' do let(:mutation_class) { ::Mutations::Issues::Create } end + + context 'when position params are provided' do + let(:existing_issue) { create(:issue, project: project, relative_position: 50) } + + before do + input.merge!( + move_after_id: existing_issue.to_global_id.to_s + ) + end + + it 'sets the correct position' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['issue']['relativePosition']).to be < existing_issue.relative_position + end + end end end 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 929609d4160..0c034f38dc8 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 @@ -12,7 +12,7 @@ RSpec.describe 'ConfigureSastIac' do let(:mutation_response) { graphql_mutation_response(:configureSastIac) } context 'when authorized' do - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } it 'creates a branch with sast iac configured' do post_graphql_mutation(mutation, current_user: user) 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 23a154b71a0..8fa6e44b208 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 @@ -12,7 +12,7 @@ RSpec.describe 'ConfigureSecretDetection' do let(:mutation_response) { graphql_mutation_response(:configureSecretDetection) } context 'when authorized' do - let_it_be(:user) { project.owner } + let_it_be(:user) { project.first_owner } it 'creates a branch with secret detection configured' do post_graphql_mutation(mutation, current_user: 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 new file mode 100644 index 00000000000..e1c7fd9d60d --- /dev/null +++ b/spec/requests/api/graphql/mutations/user_preferences/update_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Mutations::UserPreferences::Update do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + + let(:sort_value) { 'TITLE_ASC' } + + let(:input) do + { + 'issuesSort' => sort_value + } + end + + let(:mutation) { graphql_mutation(:userPreferencesUpdate, input) } + let(:mutation_response) { graphql_mutation_response(:userPreferencesUpdate) } + + context 'when user has no existing preference' do + it 'creates the user preference record' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['userPreferences']['issuesSort']).to eq(sort_value) + + expect(current_user.user_preference.persisted?).to eq(true) + expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s) + end + end + + context 'when user has existing preference' do + before do + current_user.create_user_preference!(issues_sort: Types::IssueSortEnum.values['TITLE_DESC'].value) + end + + it 'updates the existing value' do + post_graphql_mutation(mutation, current_user: current_user) + + current_user.user_preference.reload + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['userPreferences']['issuesSort']).to eq(sort_value) + + expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s) + end + end +end 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 e7a0c7753fb..6abdaa2c850 100644 --- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb @@ -47,6 +47,18 @@ RSpec.describe 'Create a work item' do ) end + context 'when input is invalid' do + let(:input) { { 'title' => '', 'workItemTypeId' => WorkItems::Type.default_by_type(:task).to_global_id.to_s } } + + it 'does not create and returns validation errors' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to not_change(WorkItem, :count) + + expect(graphql_mutation_response(:work_item_create)['errors']).to contain_exactly("Title can't be blank") + end + end + it_behaves_like 'has spam protection' do let(:mutation_class) { ::Mutations::WorkItems::Create } end @@ -56,8 +68,13 @@ RSpec.describe 'Create a work item' do stub_feature_flags(work_items: false) end - it_behaves_like 'a mutation that returns top-level errors', - errors: ["Field 'workItemCreate' doesn't exist on type 'Mutation'", "Variable $workItemCreateInput is declared by anonymous mutation but not used"] + it 'does not create the work item and returns an error' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to not_change(WorkItem, :count) + + expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project') + end end 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 new file mode 100644 index 00000000000..14c8b757a57 --- /dev/null +++ b/spec/requests/api/graphql/mutations/work_items/delete_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Delete a work item' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } + + let(:current_user) { developer } + let(:mutation) { graphql_mutation(:workItemDelete, { 'id' => work_item.to_global_id.to_s }) } + let(:mutation_response) { graphql_mutation_response(:work_item_delete) } + + context 'when the user is not allowed to delete a work item' do + let(:work_item) { create(:work_item, project: project) } + + it_behaves_like 'a mutation that returns a top-level access error' + end + + context 'when user has permissions to delete a work item' do + let_it_be(:authored_work_item, refind: true) { create(:work_item, project: project, author: developer, assignees: [developer]) } + + let(:work_item) { authored_work_item } + + it 'deletes the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change(WorkItem, :count).by(-1) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['project']).to include('id' => work_item.project.to_global_id.to_s) + end + + context 'when the work_items feature flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'does not delete the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to not_change(WorkItem, :count) + + expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project') + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb new file mode 100644 index 00000000000..71b03103115 --- /dev/null +++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Update a work item' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } + let_it_be(:work_item, refind: true) { create(:work_item, project: project) } + + let(:work_item_event) { 'CLOSE' } + let(:input) { { 'stateEvent' => work_item_event, 'title' => 'updated title' } } + + let(:mutation) { graphql_mutation(:workItemUpdate, input.merge('id' => work_item.to_global_id.to_s)) } + + let(:mutation_response) { graphql_mutation_response(:work_item_update) } + + context 'the user is not allowed to update a work item' do + let(:current_user) { create(:user) } + + it_behaves_like 'a mutation that returns a top-level access error' + end + + context 'when user has permissions to update a work item' do + let(:current_user) { developer } + + context 'when the work item is open' do + it 'closes and updates the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to change(work_item, :state).from('opened').to('closed').and( + change(work_item, :title).from(work_item.title).to('updated title') + ) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['workItem']).to include( + 'state' => 'CLOSED', + 'title' => 'updated title' + ) + end + end + + context 'when the work item is closed' do + let(:work_item_event) { 'REOPEN' } + + before do + work_item.close! + end + + it 'reopens the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to change(work_item, :state).from('closed').to('opened') + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['workItem']).to include( + 'state' => 'OPEN' + ) + end + end + + it_behaves_like 'has spam protection' do + let(:mutation_class) { ::Mutations::WorkItems::Update } + end + + context 'when the work_items feature flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'does not update the work item and returns and error' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to not_change(work_item, :title) + + expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project') + end + end + end +end diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 2ff3bc7cc47..365efc514d4 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -102,18 +102,6 @@ RSpec.describe 'package details' do expect(package_file_ids).to contain_exactly(package_file.to_global_id.to_s) end - - context 'with packages_installable_package_files disabled' do - before do - stub_feature_flags(packages_installable_package_files: false) - end - - it 'returns them' do - subject - - expect(package_file_ids).to contain_exactly(package_file_pending_destruction.to_global_id.to_s, package_file.to_global_id.to_s) - end - end end context 'with a batched query' do @@ -145,8 +133,9 @@ RSpec.describe 'package details' do let(:pipeline_gids) { pipelines.sort_by(&:id).map(&:to_gid).map(&:to_s).reverse } before do - composer_package.pipelines = pipelines - composer_package.save! + pipelines.each do |pipeline| + create(:package_build_info, package: composer_package, pipeline: pipeline) + end end def run_query(args) 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 dc16847a669..e3ea9e46353 100644 --- a/spec/requests/api/graphql/project/container_expiration_policy_spec.rb +++ b/spec/requests/api/graphql/project/container_expiration_policy_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'getting a repository in a project' do include GraphqlHelpers let_it_be(:project) { create(:project) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let_it_be(:container_expiration_policy) { project.container_expiration_policy } let(:fields) do diff --git a/spec/requests/api/graphql/project/container_repositories_spec.rb b/spec/requests/api/graphql/project/container_repositories_spec.rb index 692143b2215..bbab6012f3f 100644 --- a/spec/requests/api/graphql/project/container_repositories_spec.rb +++ b/spec/requests/api/graphql/project/container_repositories_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'getting container repositories in a project' do ) end - let(:user) { project.owner } + let(:user) { project.first_owner } let(:variables) { {} } let(:container_repositories_response) { graphql_data.dig('project', 'containerRepositories', 'edges') } let(:container_repositories_count_response) { graphql_data.dig('project', 'containerRepositoriesCount') } 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 40a3281d3b7..2b85704f479 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 @@ -6,7 +6,7 @@ RSpec.describe 'getting a detailed sentry error' do let_it_be(:project) { create(:project, :repository) } let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let_it_be(:sentry_detailed_error) { build(:error_tracking_sentry_detailed_error) } let(:sentry_gid) { sentry_detailed_error.to_global_id.to_s } 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 a540386a9de..3ca0e35882a 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 @@ -6,7 +6,7 @@ RSpec.describe 'sentry errors requests' do let_it_be(:project) { create(:project, :repository) } let_it_be(:project_setting) { create(:project_error_tracking_setting, project: project) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let(:query) do graphql_query_for( diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb index 9b24698f40c..e7534945e7a 100644 --- a/spec/requests/api/graphql/project/grafana_integration_spec.rb +++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'Getting Grafana Integration' do include GraphqlHelpers let_it_be(:project) { create(:project, :repository) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let_it_be(:grafana_integration) { create(:grafana_integration, project: project) } let(:fields) do 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 9d98498ca8a..46fd65db1c5 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 @@ -24,7 +24,7 @@ RSpec.describe 'Getting versions related to an issue' do create(:design_version, issue: issue) end - let_it_be(:owner) { issue.project.owner } + let_it_be(:owner) { issue.project.first_owner } def version_query(params = version_params) query_graphql_field(:versions, params, version_query_fields) 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 def41efddde..f0205319983 100644 --- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Getting designs related to an issue' do include DesignManagementTestHelpers let_it_be(:design) { create(:design, :with_smaller_image_versions, versions_count: 1) } - let_it_be(:current_user) { design.project.owner } + let_it_be(:current_user) { design.project.first_owner } let(:design_query) do <<~NODE 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 7148750b6cb..de2ace95757 100644 --- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Getting designs related to an issue' do let_it_be(:project) { create(:project, :public) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:design) { create(:design, :with_file, versions_count: 1, issue: issue) } - let_it_be(:current_user) { project.owner } + let_it_be(:current_user) { project.first_owner } let_it_be(:note) { create(:diff_note_on_design, noteable: design, project: project) } before do diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index b0bedd99fce..303748bc70e 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -29,6 +29,10 @@ RSpec.describe 'getting merge request listings nested in a project' do create(:merge_request, :unique_branches, source_project: project) end + let(:all_merge_requests) do + [merge_request_a, merge_request_b, merge_request_c, merge_request_d, merge_request_e] + end + let(:results) { graphql_data.dig('project', 'mergeRequests', 'nodes') } let(:search_params) { nil } @@ -180,6 +184,39 @@ RSpec.describe 'getting merge request listings nested in a project' do it_behaves_like 'when searching with parameters' end + context 'when searching by update time' do + let(:start_time) { 10.days.ago } + let(:cutoff) { start_time + 36.hours } + + before do + all_merge_requests.each_with_index do |mr, i| + mr.updated_at = start_time + i.days + mr.save!(touch: false) + end + end + + context 'when searching by updated_after' do + let(:search_params) { { updated_after: cutoff } } + let(:mrs) { all_merge_requests[2..] } + + it_behaves_like 'when searching with parameters' + end + + context 'when searching by updated_before' do + let(:search_params) { { updated_before: cutoff } } + let(:mrs) { all_merge_requests[0..1] } + + it_behaves_like 'when searching with parameters' + end + + context 'when searching by updated_before and updated_after' do + let(:search_params) { { updated_after: cutoff, updated_before: cutoff + 2.days } } + let(:mrs) { all_merge_requests[2..3] } + + it_behaves_like 'when searching with parameters' + end + end + context 'when searching by combination' do let(:search_params) { { state: :closed, labels: [label.title] } } let(:mrs) { [merge_request_c] } diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb index 466464f600c..315d44884ff 100644 --- a/spec/requests/api/graphql/project/project_members_spec.rb +++ b/spec/requests/api/graphql/project/project_members_spec.rb @@ -110,6 +110,102 @@ RSpec.describe 'getting project members information' do end end + context 'merge request interactions' do + let(:project_path) { var('ID!').with(parent_project.full_path) } + let(:mr_a) do + var('MergeRequestID!') + .with(global_id_of(create(:merge_request, source_project: parent_project, source_branch: 'branch-1'))) + end + + let(:mr_b) do + var('MergeRequestID!') + .with(global_id_of(create(:merge_request, source_project: parent_project, source_branch: 'branch-2'))) + end + + let(:interaction_query) do + <<~HEREDOC + edges { + node { + user { + id + } + mrA: #{query_graphql_field(:merge_request_interaction, { id: mr_a }, 'canMerge')} + } + } + HEREDOC + end + + let(:interaction_b_query) do + <<~HEREDOC + edges { + node { + user { + id + } + mrA: #{query_graphql_field(:merge_request_interaction, { id: mr_a }, 'canMerge')} + mrB: #{query_graphql_field(:merge_request_interaction, { id: mr_b }, 'canMerge')} + } + } + HEREDOC + end + + it 'avoids N+1 queries, when requesting multiple MRs' do + control_query = with_signature( + [project_path, mr_a], + graphql_query_for(:project, { full_path: project_path }, + query_graphql_field(:project_members, nil, interaction_query)) + ) + query_two = with_signature( + [project_path, mr_a, mr_b], + graphql_query_for(:project, { full_path: project_path }, + query_graphql_field(:project_members, nil, interaction_b_query)) + ) + + control_count = ActiveRecord::QueryRecorder.new do + post_graphql(control_query, current_user: user, variables: [project_path, mr_a]) + end + + # two project members, neither of whom can merge + expect(can_merge(:mrA)).to eq [false, false] + + expect do + post_graphql(query_two, current_user: user, variables: [project_path, mr_a, mr_b]) + + expect(can_merge(:mrA)).to eq [false, false] + expect(can_merge(:mrB)).to eq [false, false] + end.not_to exceed_query_limit(control_count) + end + + it 'avoids N+1 queries, when more users are involved' do + new_user = create(:user) + + query = with_signature( + [project_path, mr_a], + graphql_query_for(:project, { full_path: project_path }, + query_graphql_field(:project_members, nil, interaction_query)) + ) + + control_count = ActiveRecord::QueryRecorder.new do + post_graphql(query, current_user: user, variables: [project_path, mr_a]) + end + + # two project members, neither of whom can merge + expect(can_merge(:mrA)).to eq [false, false] + + parent_project.add_guest(new_user) + + expect do + post_graphql(query, current_user: user, variables: [project_path, mr_a]) + + expect(can_merge(:mrA)).to eq [false, false, false] + end.not_to exceed_query_limit(control_count) + end + + def can_merge(name) + graphql_data_at(:project, :project_members, :edges, :node, name, :can_merge) + end + end + context 'when unauthenticated' do it 'returns members' do fetch_members(current_user: nil, project: parent_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 new file mode 100644 index 00000000000..b3daf86c4af --- /dev/null +++ b/spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting project recent issue boards' do + include GraphqlHelpers + + it_behaves_like 'querying a GraphQL type recent boards' do + let_it_be(:user) { create(:user) } + let_it_be(:parent) { create(:project, :public, namespace: user.namespace) } + let_it_be(:board) { create(:board, resource_parent: parent, name: 'test project board') } + let(:board_type) { 'project' } + end +end diff --git a/spec/requests/api/graphql/project/repository/blobs_spec.rb b/spec/requests/api/graphql/project/repository/blobs_spec.rb index 12f6fbd793e..ba87f1100f2 100644 --- a/spec/requests/api/graphql/project/repository/blobs_spec.rb +++ b/spec/requests/api/graphql/project/repository/blobs_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'getting blobs in a project repository' do include GraphqlHelpers let(:project) { create(:project, :repository) } - let(:current_user) { project.owner } + let(:current_user) { project.first_owner } let(:paths) { ["CONTRIBUTING.md", "README.md"] } let(:ref) { project.default_branch } let(:fields) do diff --git a/spec/requests/api/graphql/project/repository_spec.rb b/spec/requests/api/graphql/project/repository_spec.rb index 8810f2fa3d5..b00f64c3db6 100644 --- a/spec/requests/api/graphql/project/repository_spec.rb +++ b/spec/requests/api/graphql/project/repository_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'getting a repository in a project' do include GraphqlHelpers let(:project) { create(:project, :repository) } - let(:current_user) { project.owner } + let(:current_user) { project.first_owner } let(:fields) do <<~QUERY #{all_graphql_fields_for('repository'.classify)} diff --git a/spec/requests/api/graphql/project/tree/tree_spec.rb b/spec/requests/api/graphql/project/tree/tree_spec.rb index f4cd316da96..25e878a5b1a 100644 --- a/spec/requests/api/graphql/project/tree/tree_spec.rb +++ b/spec/requests/api/graphql/project/tree/tree_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'getting a tree in a project' do include GraphqlHelpers let(:project) { create(:project, :repository) } - let(:current_user) { project.owner } + let(:current_user) { project.first_owner } let(:path) { "" } let(:ref) { "master" } let(:fields) do |