diff options
Diffstat (limited to 'spec/requests/api/graphql')
60 files changed, 1161 insertions, 387 deletions
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 e8fb9daa43b..eb206465bce 100644 --- a/spec/requests/api/graphql/boards/board_lists_query_spec.rb +++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb @@ -69,6 +69,10 @@ RSpec.describe 'get board lists' do let(:data_path) { [board_parent_type, :boards, :nodes, 0, :lists] } + def pagination_results_data(lists) + lists + end + def pagination_query(params) graphql_query_for( board_parent_type, @@ -94,7 +98,7 @@ RSpec.describe 'get board lists' do it_behaves_like 'sorted paginated query' do let(:sort_param) { } let(:first_param) { 2 } - let(:all_records) { lists.map { |list| global_id_of(list) } } + let(:all_records) { lists.map { |list| a_graphql_entity_for(list) } } end end end diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb index 62b15a8396c..5f8a895b16e 100644 --- a/spec/requests/api/graphql/ci/config_spec.rb +++ b/spec/requests/api/graphql/ci/config_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe 'Query.ciConfig' do include GraphqlHelpers + include StubRequests subject(:post_graphql_query) { post_graphql(query, current_user: user) } @@ -57,6 +58,16 @@ RSpec.describe 'Query.ciConfig' do } } } + mergedYaml + includes { + type + location + blob + raw + extra + contextProject + contextSha + } } } ) @@ -71,10 +82,12 @@ RSpec.describe 'Query.ciConfig' do it 'returns the correct structure' do post_graphql_query - expect(graphql_data['ciConfig']).to eq( + expect(graphql_data['ciConfig']).to include( "status" => "VALID", "errors" => [], "warnings" => [], + "includes" => [], + "mergedYaml" => a_kind_of(String), "stages" => { "nodes" => @@ -222,24 +235,6 @@ RSpec.describe 'Query.ciConfig' do ) end - context 'when using deprecated keywords' do - let_it_be(:content) do - YAML.dump( - rspec: { script: 'ls', type: 'test' }, - types: ['test'] - ) - end - - 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.', - 'jobs:rspec `type` is deprecated in 9.0 and will be removed in 15.0.' - ) - end - end - context 'when the config file includes other files' do let_it_be(:content) do YAML.dump( @@ -271,6 +266,18 @@ RSpec.describe 'Query.ciConfig' do "status" => "VALID", "errors" => [], "warnings" => [], + "includes" => [ + { + "type" => "local", + "location" => "other_file.yml", + "blob" => "http://localhost/#{project.full_path}/-/blob/#{project.commit.sha}/other_file.yml", + "raw" => "http://localhost/#{project.full_path}/-/raw/#{project.commit.sha}/other_file.yml", + "extra" => {}, + "contextProject" => project.full_path, + "contextSha" => project.commit.sha + } + ], + "mergedYaml" => "---\nbuild:\n script: build\nrspec:\n script: rspec\n", "stages" => { "nodes" => @@ -302,7 +309,7 @@ RSpec.describe 'Query.ciConfig' do "when" => "on_success", "tags" => [], "needs" => { "nodes" => [] } -} + } ] } }, @@ -337,4 +344,101 @@ RSpec.describe 'Query.ciConfig' do ) end end + + context 'when the config file has multiple includes' do + let_it_be(:other_project) { create(:project, :repository, creator: user, namespace: user.namespace) } + + let_it_be(:content) do + YAML.dump( + include: [ + { local: 'other_file.yml' }, + { remote: 'https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml' }, + { file: 'other_project_file.yml', project: other_project.full_path }, + { template: 'Jobs/Build.gitlab-ci.yml' } + ], + rspec: { + script: 'rspec' + } + ) + end + + let(:remote_file_content) do + YAML.dump( + remote_file_test: { + script: 'remote_file_test' + } + ) + end + + before do + allow_next_instance_of(Repository) do |repository| + allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_file.yml') do + YAML.dump( + build: { + script: 'build' + } + ) + end + + allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_project_file.yml') do + YAML.dump( + other_project_test: { + script: 'other_project_test' + } + ) + end + end + + stub_full_request('https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml').to_return(body: remote_file_content) + + post_graphql_query + end + + it_behaves_like 'a working graphql query' + + # rubocop:disable Layout/LineLength + it 'returns correct includes' do + expect(graphql_data['ciConfig']["includes"]).to eq( + [ + { + "type" => "local", + "location" => "other_file.yml", + "blob" => "http://localhost/#{project.full_path}/-/blob/#{project.commit.sha}/other_file.yml", + "raw" => "http://localhost/#{project.full_path}/-/raw/#{project.commit.sha}/other_file.yml", + "extra" => {}, + "contextProject" => project.full_path, + "contextSha" => project.commit.sha + }, + { + "type" => "remote", + "location" => "https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml", + "blob" => nil, + "raw" => "https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml", + "extra" => {}, + "contextProject" => project.full_path, + "contextSha" => project.commit.sha + }, + { + "type" => "file", + "location" => "other_project_file.yml", + "blob" => "http://localhost/#{other_project.full_path}/-/blob/#{other_project.commit.sha}/other_project_file.yml", + "raw" => "http://localhost/#{other_project.full_path}/-/raw/#{other_project.commit.sha}/other_project_file.yml", + "extra" => { "project" => other_project.full_path, "ref" => "HEAD" }, + "contextProject" => project.full_path, + "contextSha" => project.commit.sha + }, + { + "type" => "template", + "location" => "Jobs/Build.gitlab-ci.yml", + "blob" => nil, + "raw" => "https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml", + "extra" => {}, + "contextProject" => project.full_path, + "contextSha" => project.commit.sha + } + ] + ) + end + # rubocop:enable Layout/LineLength + end end diff --git a/spec/requests/api/graphql/ci/job_spec.rb b/spec/requests/api/graphql/ci/job_spec.rb index ddb2664d353..2fb90dcd92b 100644 --- a/spec/requests/api/graphql/ci/job_spec.rb +++ b/spec/requests/api/graphql/ci/job_spec.rb @@ -47,10 +47,8 @@ RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do ) post_graphql(query, current_user: user) - expect(graphql_data_at(*path)).to match a_hash_including( - 'id' => global_id_of(job_2), - 'name' => job_2.name, - 'allowFailure' => job_2.allow_failure, + expect(graphql_data_at(*path)).to match a_graphql_entity_for( + job_2, :name, :allow_failure, 'duration' => 25, 'kind' => 'BUILD', 'queuedDuration' => 2.0, @@ -66,10 +64,7 @@ RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do it 'retrieves scalar fields' do post_graphql(query, current_user: user) - expect(graphql_data_at(*path)).to match a_hash_including( - 'id' => global_id_of(job_2), - 'name' => job_2.name - ) + expect(graphql_data_at(*path)).to match a_graphql_entity_for(job_2, :name) end end end @@ -102,8 +97,8 @@ RSpec.describe 'Query.project(fullPath).pipelines.job(id)' do 'name' => test_stage.name, 'jobs' => a_hash_including( 'nodes' => contain_exactly( - a_hash_including('id' => global_id_of(job_2)), - a_hash_including('id' => global_id_of(job_3)) + a_graphql_entity_for(job_2), + a_graphql_entity_for(job_3) ) ) ) diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb index 39f0f696b08..6fa455cbfca 100644 --- a/spec/requests/api/graphql/ci/runner_spec.rb +++ b/spec/requests/api/graphql/ci/runner_spec.rb @@ -11,7 +11,8 @@ RSpec.describe 'Query.runner(id)' do let_it_be(:active_instance_runner) do create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago, active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600, - access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom) + access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom, + maintenance_note: 'Test maintenance note') end let_it_be(:inactive_instance_runner) do @@ -27,10 +28,6 @@ RSpec.describe 'Query.runner(id)' do let_it_be(:active_project_runner) { create(:ci_runner, :project) } - before do - allow(Gitlab::Ci::RunnerUpgradeCheck.instance).to receive(:check_runner_upgrade_status) - end - shared_examples 'runner details fetch' do let(:query) do wrap_fields(query_graphql_path(query_path, all_graphql_fields_for('CiRunner'))) @@ -66,6 +63,9 @@ RSpec.describe 'Query.runner(id)' do '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, 'jobCount' => 0, 'jobs' => a_hash_including("count" => 0, "nodes" => [], "pageInfo" => anything), 'projectCount' => nil, @@ -239,8 +239,8 @@ RSpec.describe 'Query.runner(id)' do stale_runner_data = graphql_data_at(:stale_runner) expect(stale_runner_data).to match a_hash_including( - 'status' => 'NOT_CONNECTED', - 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED', + 'status' => 'STALE', + 'legacyStatusWithExplicitVersion' => 'STALE', 'newStatus' => 'STALE' ) @@ -253,8 +253,8 @@ RSpec.describe 'Query.runner(id)' do never_contacted_instance_runner_data = graphql_data_at(:never_contacted_instance_runner) expect(never_contacted_instance_runner_data).to match a_hash_including( - 'status' => 'NOT_CONNECTED', - 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED', + 'status' => 'NEVER_CONTACTED', + 'legacyStatusWithExplicitVersion' => 'NEVER_CONTACTED', 'newStatus' => 'NEVER_CONTACTED' ) end diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb index 6b88c82b025..d3e94671724 100644 --- a/spec/requests/api/graphql/ci/runners_spec.rb +++ b/spec/requests/api/graphql/ci/runners_spec.rb @@ -56,9 +56,9 @@ RSpec.describe 'Query.runners' do it_behaves_like 'a working graphql query returning expected runner' end - context 'runner_type is PROJECT_TYPE and status is NOT_CONNECTED' do + context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do let(:runner_type) { 'PROJECT_TYPE' } - let(:status) { 'NOT_CONNECTED' } + let(:status) { 'NEVER_CONTACTED' } let!(:expected_runner) { project_runner } 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 922a9ab277e..847fa72522e 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 @@ -127,7 +127,7 @@ RSpec.describe 'container repository details' do let(:query) do <<~GQL - query($id: ID!, $n: Int) { + query($id: ContainerRepositoryID!, $n: Int) { containerRepository(id: $id) { tags(first: $n) { edges { @@ -157,7 +157,7 @@ RSpec.describe 'container repository details' do let(:query) do <<~GQL - query($id: ID!, $n: ContainerRepositoryTagSort) { + query($id: ContainerRepositoryID!, $n: ContainerRepositoryTagSort) { containerRepository(id: $id) { tags(sort: $n) { edges { @@ -194,7 +194,7 @@ RSpec.describe 'container repository details' do let(:query) do <<~GQL - query($id: ID!, $n: String) { + query($id: ContainerRepositoryID!, $n: String) { containerRepository(id: $id) { tags(name: $n) { edges { @@ -232,7 +232,7 @@ RSpec.describe 'container repository details' do let(:query) do <<~GQL - query($id: ID!) { + query($id: ContainerRepositoryID!) { containerRepository(id: $id) { size } diff --git a/spec/requests/api/graphql/current_user_todos_spec.rb b/spec/requests/api/graphql/current_user_todos_spec.rb index 7f37abba74a..da1c893ec2b 100644 --- a/spec/requests/api/graphql/current_user_todos_spec.rb +++ b/spec/requests/api/graphql/current_user_todos_spec.rb @@ -37,8 +37,8 @@ RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do post_graphql(query, current_user: current_user) expect(todoable_response).to contain_exactly( - a_hash_including('id' => global_id_of(done_todo)), - a_hash_including('id' => global_id_of(pending_todo)) + a_graphql_entity_for(done_todo), + a_graphql_entity_for(pending_todo) ) end @@ -63,7 +63,7 @@ RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do post_graphql(query, current_user: current_user) expect(todoable_response).to contain_exactly( - a_hash_including('id' => global_id_of(pending_todo)) + a_graphql_entity_for(pending_todo) ) end end @@ -75,7 +75,7 @@ RSpec.describe 'A Todoable that implements the CurrentUserTodos interface' do post_graphql(query, current_user: current_user) expect(todoable_response).to contain_exactly( - a_hash_including('id' => global_id_of(done_todo)) + a_graphql_entity_for(done_todo) ) end end 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 de3dbc5c324..d21c3046c1a 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 @@ -47,14 +47,14 @@ RSpec.describe 'getting dependency proxy settings for a group' do context 'with different permissions' do where(:group_visibility, :role, :access_granted) do :private | :maintainer | true - :private | :developer | true - :private | :reporter | true - :private | :guest | true + :private | :developer | false + :private | :reporter | false + :private | :guest | false :private | :anonymous | false :public | :maintainer | true - :public | :developer | true - :public | :reporter | true - :public | :guest | true + :public | :developer | false + :public | :reporter | false + :public | :guest | false :public | :anonymous | false end 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 c8797d84906..40f4b082072 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 @@ -46,14 +46,14 @@ RSpec.describe 'getting dependency proxy image ttl policy for a group' do context 'with different permissions' do where(:group_visibility, :role, :access_granted) do :private | :maintainer | true - :private | :developer | true - :private | :reporter | true - :private | :guest | true + :private | :developer | false + :private | :reporter | false + :private | :guest | false :private | :anonymous | false :public | :maintainer | true - :public | :developer | true - :public | :reporter | true - :public | :guest | true + :public | :developer | false + :public | :reporter | false + :public | :guest | false :public | :anonymous | false end 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 3527c8183f6..c7149c100b2 100644 --- a/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb +++ b/spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb @@ -122,12 +122,12 @@ RSpec.describe 'getting dependency proxy manifests in a group' do let(:current_user) { owner } context 'with default sorting' do - let_it_be(:descending_manifests) { manifests.reverse.map { |manifest| global_id_of(manifest)} } + let_it_be(:descending_manifests) { manifests.reverse.map { |manifest| global_id_of(manifest) } } it_behaves_like 'sorted paginated query' do let(:sort_param) { '' } let(:first_param) { 2 } - let(:all_records) { descending_manifests } + let(:all_records) { descending_manifests.map(&:to_s) } end end diff --git a/spec/requests/api/graphql/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb index 78852622835..fec866486ae 100644 --- a/spec/requests/api/graphql/group/group_members_spec.rb +++ b/spec/requests/api/graphql/group/group_members_spec.rb @@ -24,8 +24,8 @@ RSpec.describe 'getting group members information' do expect(graphql_errors).to be_nil expect(graphql_data_at(:group, :group_members, :edges, :node)).to contain_exactly( - { 'user' => { 'id' => global_id_of(user_1) } }, - { 'user' => { 'id' => global_id_of(user_2) } }, + { 'user' => a_graphql_entity_for(user_1) }, + { 'user' => a_graphql_entity_for(user_2) }, 'user' => nil ) end @@ -77,6 +77,48 @@ RSpec.describe 'getting group members information' do end end + context 'by access levels' do + before do + parent_group.add_owner(user_1) + parent_group.add_maintainer(user_2) + end + + subject(:by_access_levels) { fetch_members(group: parent_group, args: { access_levels: access_levels }) } + + context 'by owner' do + let(:access_levels) { :OWNER } + + it 'returns owner' do + by_access_levels + + expect(graphql_errors).to be_nil + expect_array_response(user_1) + end + end + + context 'by maintainer' do + let(:access_levels) { :MAINTAINER } + + it 'returns maintainer' do + by_access_levels + + expect(graphql_errors).to be_nil + expect_array_response(user_2) + end + end + + context 'by owner and maintainer' do + let(:access_levels) { [:OWNER, :MAINTAINER] } + + it 'returns owner and maintainer' do + by_access_levels + + expect(graphql_errors).to be_nil + expect_array_response(user_1, user_2) + end + end + end + context 'member relations' do let_it_be(:child_group) { create(:group, :public, parent: parent_group) } let_it_be(:grandchild_group) { create(:group, :public, parent: child_group) } @@ -182,8 +224,8 @@ RSpec.describe 'getting group members information' do def expect_array_response(*items) expect(response).to have_gitlab_http_status(:success) - member_gids = graphql_data_at(:group, :group_members, :edges, :node, :user, :id) + members = graphql_data_at(:group, :group_members, :edges, :node, :user) - expect(member_gids).to match_array(items.map { |u| global_id_of(u) }) + expect(members).to match_array(items.map { |u| a_graphql_entity_for(u) }) end end diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb index c0faff11c8d..434b0d16569 100644 --- a/spec/requests/api/graphql/group/merge_requests_spec.rb +++ b/spec/requests/api/graphql/group/merge_requests_spec.rb @@ -39,7 +39,7 @@ RSpec.describe 'Query.group.mergeRequests' do end def expected_mrs(mrs) - mrs.map { |mr| a_hash_including('id' => global_id_of(mr)) } + mrs.map { |mr| a_graphql_entity_for(mr) } end describe 'not passing any arguments' do diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb index 2b80b5239c8..7c51409f907 100644 --- a/spec/requests/api/graphql/group/milestones_spec.rb +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -170,10 +170,8 @@ RSpec.describe 'Milestones through GroupQuery' do end it 'returns correct values for scalar fields' do - expect(post_query).to eq({ - 'id' => global_id_of(milestone), - 'title' => milestone.title, - 'description' => milestone.description, + expect(post_query).to match a_graphql_entity_for( + milestone, :title, :description, 'state' => 'active', 'webPath' => milestone_path(milestone), 'dueDate' => milestone.due_date.iso8601, @@ -183,7 +181,7 @@ RSpec.describe 'Milestones through GroupQuery' do 'projectMilestone' => false, 'groupMilestone' => true, 'subgroupMilestone' => false - }) + ) end context 'milestone statistics' do diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb index 42ca3348384..05fd6bf3022 100644 --- a/spec/requests/api/graphql/issue/issue_spec.rb +++ b/spec/requests/api/graphql/issue/issue_spec.rb @@ -8,8 +8,8 @@ RSpec.describe 'Query.issue(id)' do let_it_be(:project) { create(:project) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:current_user) { create(:user) } - let_it_be(:issue_params) { { 'id' => issue.to_global_id.to_s } } + let(:issue_params) { { 'id' => global_id_of(issue) } } let(:issue_data) { graphql_data['issue'] } let(:issue_fields) { all_graphql_fields_for('Issue'.classify) } @@ -100,7 +100,8 @@ RSpec.describe 'Query.issue(id)' do let_it_be(:issue_fields) { ['moved', 'movedTo { title }'] } let_it_be(:new_issue) { create(:issue) } let_it_be(:issue) { create(:issue, project: project, moved_to: new_issue) } - let_it_be(:issue_params) { { 'id' => issue.to_global_id.to_s } } + + let(:issue_params) { { 'id' => global_id_of(issue) } } before_all do new_issue.project.add_developer(current_user) 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 75dd01a0763..d89f381753e 100644 --- a/spec/requests/api/graphql/merge_request/merge_request_spec.rb +++ b/spec/requests/api/graphql/merge_request/merge_request_spec.rb @@ -8,8 +8,8 @@ RSpec.describe 'Query.merge_request(id)' do let_it_be(:project) { create(:project, :empty_repo) } let_it_be(:merge_request) { create(:merge_request, source_project: project) } let_it_be(:current_user) { create(:user) } - let_it_be(:merge_request_params) { { 'id' => merge_request.to_global_id.to_s } } + let(:merge_request_params) { { 'id' => global_id_of(merge_request) } } let(:merge_request_data) { graphql_data['mergeRequest'] } let(:merge_request_fields) { all_graphql_fields_for('MergeRequest'.classify) } diff --git a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb index 30e7f196542..394d9ff53d1 100644 --- a/spec/requests/api/graphql/mutations/ci/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 'CiCdSettingsUpdate' do +RSpec.describe 'ProjectCiCdSettingsUpdate' 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 12368e7e9c5..6818ba33e74 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 @@ -64,11 +64,10 @@ RSpec.describe 'RunnersRegistrationTokenReset' do context 'applied to project' do let_it_be(:project) { create_default(:project) } + let(:target) { project } let(:input) { { type: 'PROJECT_TYPE', id: project.to_global_id.to_s } } - include_context 'when unauthorized', 'project' do - let(:target) { project } - end + include_context('when unauthorized', 'project') include_context 'when authorized', 'project' do let_it_be(:user) { project.first_owner } @@ -82,11 +81,10 @@ RSpec.describe 'RunnersRegistrationTokenReset' do context 'applied to group' do let_it_be(:group) { create_default(:group) } + let(:target) { group } let(:input) { { type: 'GROUP_TYPE', id: group.to_global_id.to_s } } - include_context 'when unauthorized', 'group' do - let(:target) { group } - end + include_context('when unauthorized', 'group') include_context 'when authorized', 'group' do let_it_be(:user) { create_default(:group_member, :owner, user: create(:user), group: group ).user } @@ -99,10 +97,12 @@ RSpec.describe 'RunnersRegistrationTokenReset' do context 'applied to instance' do before do - ApplicationSetting.create_from_defaults + target stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end + let_it_be(:target) { ApplicationSetting.create_from_defaults } + let(:input) { { type: 'INSTANCE_TYPE' } } context 'when unauthorized' do 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 5f6822223ca..4891e64aab8 100644 --- a/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb +++ b/spec/requests/api/graphql/mutations/clusters/agents/delete_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Delete a cluster agent' do 'or you don\'t have permission to perform this action'] it 'does not delete cluster agent' do - expect { cluster_agent.reload }.not_to raise_error(ActiveRecord::RecordNotFound) + expect { cluster_agent.reload }.not_to raise_error end end 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 0156142dc6f..ca7c1b2ce5f 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 @@ -135,7 +135,7 @@ RSpec.describe 'Updating the container expiration policy' do context 'with existing container expiration policy' do where(:user_role, :shared_examples_name) do :maintainer | 'accepting the mutation request updating the container expiration policy' - :developer | 'accepting the mutation request updating the container expiration policy' + :developer | 'denying the mutation request' :reporter | 'denying the mutation request' :guest | 'denying the mutation request' :anonymous | 'denying the mutation request' @@ -155,7 +155,7 @@ RSpec.describe 'Updating the container expiration policy' do where(:user_role, :shared_examples_name) do :maintainer | 'accepting the mutation request creating the container expiration policy' - :developer | 'accepting the mutation request creating the container expiration policy' + :developer | 'denying the mutation request' :reporter | 'denying the mutation request' :guest | 'denying the mutation request' :anonymous | 'denying the mutation request' 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 f05bf23ad27..9eb13e534ac 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 @@ -50,7 +50,7 @@ RSpec.describe 'Updating the dependency proxy group settings' do context 'with permission' do before do - group.add_developer(user) + group.add_maintainer(user) end it 'returns the updated dependency proxy settings', :aggregate_failures do 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 c9e9a22ee0b..31ba7ecdf0e 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 @@ -52,7 +52,7 @@ RSpec.describe 'Updating the dependency proxy image ttl policy' do context 'with permission' do before do - group.add_developer(user) + group.add_maintainer(user) end it 'returns the updated dependency proxy image ttl policy', :aggregate_failures do 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 new file mode 100644 index 00000000000..3ea8b38e20f --- /dev/null +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/create_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Creating an incident timeline event' 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(:event_occurred_at) { Time.current } + let_it_be(:note) { 'demo note' } + + let(:input) { { incident_id: incident.to_global_id.to_s, note: note, occurred_at: event_occurred_at } } + let(:mutation) do + graphql_mutation(:timeline_event_create, input) do + <<~QL + clientMutationId + errors + timelineEvent { + id + author { id username } + incident { id title } + note + editable + action + occurredAt + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:timeline_event_create) } + + before do + project.add_developer(user) + end + + it 'creates incident timeline event', :aggregate_failures do + post_graphql_mutation(mutation, current_user: user) + + timeline_event_response = mutation_response['timelineEvent'] + + expect(response).to have_gitlab_http_status(:success) + expect(timeline_event_response).to include( + 'author' => { + 'id' => user.to_global_id.to_s, + 'username' => user.username + }, + 'incident' => { + 'id' => incident.to_global_id.to_s, + 'title' => incident.title + }, + 'note' => note, + 'action' => 'comment', + 'editable' => false, + 'occurredAt' => event_occurred_at.iso8601 + ) + end +end 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 new file mode 100644 index 00000000000..faff3bfe23a --- /dev/null +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/destroy_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Removing an incident timeline event' 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(:timeline_event) { create(:incident_management_timeline_event, incident: incident, project: project) } + + let(:variables) { { id: timeline_event.to_global_id.to_s } } + + let(:mutation) do + graphql_mutation(:timeline_event_destroy, variables) do + <<~QL + clientMutationId + errors + timelineEvent { + id + author { id username } + incident { id title } + note + noteHtml + editable + action + occurredAt + createdAt + updatedAt + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:timeline_event_destroy) } + + before do + project.add_developer(user) + end + + it 'removes incident timeline event', :aggregate_failures do + post_graphql_mutation(mutation, current_user: user) + + timeline_event_response = mutation_response['timelineEvent'] + + expect(response).to have_gitlab_http_status(:success) + expect(timeline_event_response).to include( + 'author' => { + 'id' => timeline_event.author.to_global_id.to_s, + 'username' => timeline_event.author.username + }, + 'incident' => { + 'id' => incident.to_global_id.to_s, + 'title' => incident.title + }, + 'note' => timeline_event.note, + 'noteHtml' => timeline_event.note_html, + 'editable' => false, + 'action' => timeline_event.action, + 'occurredAt' => timeline_event.occurred_at.iso8601, + 'createdAt' => timeline_event.created_at.iso8601, + 'updatedAt' => timeline_event.updated_at.iso8601 + ) + expect { timeline_event.reload }.to raise_error ActiveRecord::RecordNotFound + end +end 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 new file mode 100644 index 00000000000..b92f6af1d3d --- /dev/null +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Promote an incident timeline event from a comment' 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(:comment) { create(:note, project: project, noteable: incident) } + + let(:input) { { note_id: comment.to_global_id.to_s } } + let(:mutation) do + graphql_mutation(:timeline_event_promote_from_note, input) do + <<~QL + clientMutationId + errors + timelineEvent { + author { id username } + incident { id title } + promotedFromNote { id } + note + action + editable + occurredAt + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:timeline_event_promote_from_note) } + + before do + project.add_developer(user) + end + + it 'creates incident timeline event from the note', :aggregate_failures do + post_graphql_mutation(mutation, current_user: user) + + timeline_event_response = mutation_response['timelineEvent'] + + expect(response).to have_gitlab_http_status(:success) + expect(timeline_event_response).to include( + 'author' => { + 'id' => user.to_global_id.to_s, + 'username' => user.username + }, + 'incident' => { + 'id' => incident.to_global_id.to_s, + 'title' => incident.title + }, + 'promotedFromNote' => { + 'id' => comment.to_global_id.to_s + }, + 'note' => comment.note, + 'action' => 'comment', + 'editable' => false, + 'occurredAt' => comment.created_at.iso8601 + ) + end +end 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 new file mode 100644 index 00000000000..1c4439cec6f --- /dev/null +++ b/spec/requests/api/graphql/mutations/incident_management/timeline_event/update_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Updating an incident timeline event' 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_with_reload(:timeline_event) do + create(:incident_management_timeline_event, incident: incident, project: project) + end + + let(:occurred_at) { 1.minute.ago.iso8601 } + + let(:variables) do + { + id: timeline_event.to_global_id.to_s, + note: 'Updated note', + occurred_at: occurred_at + } + end + + let(:mutation) do + graphql_mutation(:timeline_event_update, variables) do + <<~QL + clientMutationId + errors + timelineEvent { + id + author { id username } + updatedByUser { id username } + incident { id title } + note + noteHtml + occurredAt + createdAt + updatedAt + } + QL + end + end + + let(:mutation_response) { graphql_mutation_response(:timeline_event_update) } + + before do + project.add_developer(user) + end + + it 'updates the timeline event', :aggregate_failures do + post_graphql_mutation(mutation, current_user: user) + + timeline_event_response = mutation_response['timelineEvent'] + + timeline_event.reload + + expect(response).to have_gitlab_http_status(:success) + expect(timeline_event_response).to include( + 'id' => timeline_event.to_global_id.to_s, + 'author' => { + 'id' => timeline_event.author.to_global_id.to_s, + 'username' => timeline_event.author.username + }, + 'updatedByUser' => { + 'id' => user.to_global_id.to_s, + 'username' => user.username + }, + 'incident' => { + 'id' => incident.to_global_id.to_s, + 'title' => incident.title + }, + 'note' => 'Updated note', + 'noteHtml' => timeline_event.note_html, + 'occurredAt' => occurred_at, + 'createdAt' => timeline_event.created_at.iso8601, + 'updatedAt' => timeline_event.updated_at.iso8601 + ) + end +end diff --git a/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb b/spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb index 02b79dac489..715507c3cc5 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 @@ -15,7 +15,7 @@ RSpec.describe 'Setting issues crm contacts' do let(:operation_mode) { Types::MutationOperationModeEnum.default_mode } let(:initial_contacts) { contacts[0..1] } let(:mutation_contacts) { contacts[1..2] } - let(:contact_ids) { contact_global_ids(mutation_contacts) } + let(:contact_ids) { mutation_contacts.map { global_id_of(_1) } } let(:does_not_exist_or_no_permission) { "The resource that you are attempting to access does not exist or you don't have permission to perform this action" } let(:mutation) do @@ -45,8 +45,8 @@ RSpec.describe 'Setting issues crm contacts' do graphql_mutation_response(:issue_set_crm_contacts) end - def contact_global_ids(contacts) - contacts.map { |contact| global_id_of(contact) } + def expected_contacts(contacts) + contacts.map { |contact| a_graphql_entity_for(contact) } end before do @@ -58,8 +58,8 @@ RSpec.describe 'Setting issues crm contacts' do it 'updates the issue with correct contacts' do post_graphql_mutation(mutation, current_user: user) - expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes, :id)) - .to match_array(contact_global_ids(mutation_contacts)) + expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes)) + .to match_array(expected_contacts(mutation_contacts)) end end @@ -70,8 +70,8 @@ RSpec.describe 'Setting issues crm contacts' do it 'updates the issue with correct contacts' do post_graphql_mutation(mutation, current_user: user) - expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes, :id)) - .to match_array(contact_global_ids(initial_contacts + mutation_contacts)) + expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes)) + .to match_array(expected_contacts(initial_contacts + mutation_contacts)) end end @@ -82,8 +82,8 @@ RSpec.describe 'Setting issues crm contacts' do it 'updates the issue with correct contacts' do post_graphql_mutation(mutation, current_user: user) - expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes, :id)) - .to match_array(contact_global_ids(initial_contacts - mutation_contacts)) + expect(graphql_data_at(:issue_set_crm_contacts, :issue, :customer_relations_contacts, :nodes)) + .to match_array(expected_contacts(initial_contacts - mutation_contacts)) end end end @@ -117,7 +117,7 @@ RSpec.describe 'Setting issues crm contacts' do it_behaves_like 'successful mutation' context 'when the contact does not exist' do - let(:contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] } + let(:contact_ids) { [global_id_of(model_name: 'CustomerRelations::Contact', id: non_existing_record_id)] } it 'returns expected error' do post_graphql_mutation(mutation, current_user: user) @@ -159,7 +159,7 @@ RSpec.describe 'Setting issues crm contacts' do context 'when trying to remove non-existent contact' do let(:operation_mode) { Types::MutationOperationModeEnum.enum[:remove] } - let(:contact_ids) { ["gid://gitlab/CustomerRelations::Contact/#{non_existing_record_id}"] } + let(:contact_ids) { [global_id_of(model_name: 'CustomerRelations::Contact', id: non_existing_record_id)] } it 'raises expected error' do post_graphql_mutation(mutation, current_user: user) diff --git a/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb new file mode 100644 index 00000000000..9c751913827 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Request attention' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user]) } + let_it_be(:project) { merge_request.project } + + let(:input) { { user_id: global_id_of(user) } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_request_attention, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_request_attention) + end + + def mutation_errors + mutation_response['errors'] + end + + before_all do + project.add_developer(current_user) + project.add_developer(user) + end + + it 'is successful' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).to be_empty + end + + context 'when current user is not allowed to update the merge request' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + end + + context 'when user is not a reviewer' do + let(:input) { { user_id: global_id_of(create(:user)) } } + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).not_to be_empty + end + end + + context 'feature flag is disabled' do + before do + stub_feature_flags(mr_attention_requests: false) + end + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_errors[0]["message"]).to eq "Feature disabled" + end + end +end 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 d335642d321..194e42bf59d 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 @@ -109,7 +109,7 @@ RSpec.describe 'Updating the package settings' do where(:user_role, :shared_examples_name) do :maintainer | 'accepting the mutation request updating the package settings' - :developer | 'accepting the mutation request updating the package settings' + :developer | 'denying the mutation request' :reporter | 'denying the mutation request' :guest | 'denying the mutation request' :anonymous | 'denying the mutation request' @@ -131,7 +131,7 @@ RSpec.describe 'Updating the package settings' do where(:user_role, :shared_examples_name) do :maintainer | 'accepting the mutation request creating the package settings' - :developer | 'accepting the mutation request creating the package settings' + :developer | 'denying the mutation request' :reporter | 'denying the mutation request' :guest | 'denying the mutation request' :anonymous | 'denying the mutation request' 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 63b94dccca0..22b5f2d5112 100644 --- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -64,7 +64,7 @@ RSpec.describe 'Adding a Note' do it 'creates a Note in a discussion' do post_graphql_mutation(mutation, current_user: current_user) - expect(mutation_response['note']['discussion']['id']).to eq(discussion.to_global_id.to_s) + expect(mutation_response['note']['discussion']).to match a_graphql_entity_for(discussion) end context 'when the discussion_id is not for a Discussion' do @@ -109,7 +109,7 @@ RSpec.describe 'Adding a Note' do post_graphql_mutation(mutation, current_user: current_user) expect(mutation_response).to include( - 'errors' => [/Merged this merge request/], + 'errors' => include(/Merged this merge request/), 'note' => nil ) 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 89e3a71280f..0f7ccac3179 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 @@ -39,7 +39,7 @@ RSpec.describe 'Repositioning an ImageDiffNote' do post_graphql_mutation(mutation, current_user: current_user) end.to change { note.reset.position.x }.to(10) - expect(mutation_response['note']).to eq('id' => global_id_of(note)) + expect(mutation_response['note']).to match a_graphql_entity_for(note) expect(mutation_response['errors']).to be_empty end @@ -59,7 +59,7 @@ RSpec.describe 'Repositioning an ImageDiffNote' do post_graphql_mutation(mutation, current_user: current_user) end.not_to change { note.reset.position.x } - expect(mutation_response['note']).to eq('id' => global_id_of(note)) + expect(mutation_response['note']).to match a_graphql_entity_for(note) expect(mutation_response['errors']).to be_empty end end diff --git a/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb b/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb new file mode 100644 index 00000000000..053559b039d --- /dev/null +++ b/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Remove attention request' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user]) } + let_it_be(:project) { merge_request.project } + + let(:input) { { user_id: global_id_of(user) } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_remove_attention_request, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_remove_attention_request) + end + + def mutation_errors + mutation_response['errors'] + end + + before_all do + project.add_developer(current_user) + project.add_developer(user) + end + + it 'is successful' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).to be_empty + end + + context 'when current user is not allowed to update the merge request' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + end + + context 'when user is not a reviewer' do + let(:input) { { user_id: global_id_of(create(:user)) } } + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).not_to be_empty + end + end + + context 'feature flag is disabled' do + before do + stub_feature_flags(mr_attention_requests: false) + end + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_errors[0]["message"]).to eq "Feature disabled" + end + end +end diff --git a/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb new file mode 100644 index 00000000000..b674e77f093 --- /dev/null +++ b/spec/requests/api/graphql/mutations/timelogs/delete_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Delete a timelog' do + include GraphqlHelpers + let_it_be(:author) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:timelog) { create(:timelog, user: author, issue: issue, time_spent: 1800)} + + let(:current_user) { nil } + let(:mutation) { graphql_mutation(:timelogDelete, { 'id' => timelog.to_global_id.to_s }) } + let(:mutation_response) { graphql_mutation_response(:timelog_delete) } + + context 'when the user is not allowed to delete a timelog' do + let(:current_user) { create(:user) } + + before do + post_graphql_mutation(mutation, current_user: current_user) + end + + it_behaves_like 'a mutation that returns a top-level access error' + end + + context 'when user has permissions to delete a timelog' do + let(:current_user) { author } + + it 'deletes the timelog' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change(Timelog, :count).by(-1) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['timelog']).to include('id' => timelog.to_global_id.to_s) + end + end +end 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 c5c34e16717..dc20fde8e3c 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 @@ -46,8 +46,8 @@ RSpec.describe 'Marking all todos done' do expect(todo3.reload.state).to eq('done') expect(other_user_todo.reload.state).to eq('pending') - updated_todo_ids = mutation_response['todos'].map { |todo| todo['id'] } - expect(updated_todo_ids).to contain_exactly(global_id_of(todo1), global_id_of(todo3)) + updated_todos = mutation_response['todos'] + expect(updated_todos).to contain_exactly(a_graphql_entity_for(todo1), a_graphql_entity_for(todo3)) end context 'when target_id is given', :aggregate_failures do @@ -66,8 +66,8 @@ RSpec.describe 'Marking all todos done' do expect(todo1.reload.state).to eq('pending') expect(todo3.reload.state).to eq('pending') - updated_todo_ids = mutation_response['todos'].map { |todo| todo['id'] } - expect(updated_todo_ids).to contain_exactly(global_id_of(target_todo1), global_id_of(target_todo2)) + updated_todos = mutation_response['todos'] + expect(updated_todos).to contain_exactly(a_graphql_entity_for(target_todo1), a_graphql_entity_for(target_todo2)) end context 'when target does not exist' do 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 70e3cc7f5cd..4316bd060c1 100644 --- a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb @@ -11,8 +11,8 @@ RSpec.describe 'Restoring many Todos' do let_it_be(:author) { create(:user) } let_it_be(:other_user) { create(:user) } - let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :done, target: issue) } - let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) } + let_it_be_with_reload(:todo1) { create(:todo, user: current_user, author: author, state: :done, target: issue) } + let_it_be_with_reload(:todo2) { create(:todo, user: current_user, author: author, state: :done, target: issue) } let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :done) } @@ -50,8 +50,8 @@ RSpec.describe 'Restoring many Todos' do expect(mutation_response).to include( 'errors' => be_empty, 'todos' => contain_exactly( - { 'id' => global_id_of(todo1), 'state' => 'pending' }, - { 'id' => global_id_of(todo2), 'state' => 'pending' } + a_graphql_entity_for(todo1, 'state' => 'pending'), + a_graphql_entity_for(todo2, 'state' => 'pending') ) ) end 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 new file mode 100644 index 00000000000..05d3587d342 --- /dev/null +++ b/spec/requests/api/graphql/mutations/work_items/delete_task_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe "Delete a task in a work item's description" do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } + let_it_be(:task) { create(:work_item, :task, project: project, author: developer) } + let_it_be(:work_item, refind: true) do + create(:work_item, project: project, description: "- [ ] #{task.to_reference}+", lock_version: 3) + end + + before_all do + create(:issue_link, source_id: work_item.id, target_id: task.id) + end + + let(:lock_version) { work_item.lock_version } + let(:input) do + { + 'id' => work_item.to_global_id.to_s, + 'lockVersion' => lock_version, + 'taskData' => { + 'id' => task.to_global_id.to_s, + 'lineNumberStart' => 1, + 'lineNumberEnd' => 1 + } + } + end + + let(:mutation) { graphql_mutation(:workItemDeleteTask, input) } + let(:mutation_response) { graphql_mutation_response(:work_item_delete_task) } + + 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 can update the description but not delete the task' do + let(:current_user) { create(:user).tap { |u| project.add_developer(u) } } + + it_behaves_like 'a mutation that returns a top-level access error' + end + + context 'when user has permissions to remove a task' do + let(:current_user) { developer } + + it 'removes the task from the work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to change(WorkItem, :count).by(-1).and( + change(IssueLink, :count).by(-1) + ).and( + change(work_item, :description).from("- [ ] #{task.to_reference}+").to('') + ) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['workItem']).to include('id' => work_item.to_global_id.to_s) + end + + context 'when removing the task fails' do + let(:lock_version) { 2 } + + it 'makes no changes to the DB and returns an error message' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to not_change(WorkItem, :count).and( + not_change(work_item, :description) + ) + + expect(mutation_response['errors']).to contain_exactly('Stale work item. Check lock version') + end + end + + context 'when the work_items feature flag is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'does nothing and returns and 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/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb index 84c5af33e5d..1f3732980d9 100644 --- a/spec/requests/api/graphql/packages/conan_spec.rb +++ b/spec/requests/api/graphql/packages/conan_spec.rb @@ -37,22 +37,19 @@ RSpec.describe 'conan package details' do it_behaves_like 'a package with files' it 'has the correct metadata' do - expect(metadata_response).to include( - 'id' => global_id_of(package.conan_metadatum), - 'recipe' => package.conan_metadatum.recipe, - 'packageChannel' => package.conan_metadatum.package_channel, - 'packageUsername' => package.conan_metadatum.package_username, - 'recipePath' => package.conan_metadatum.recipe_path + expect(metadata_response).to match( + a_graphql_entity_for(package.conan_metadatum, + :recipe, :package_channel, :package_username, :recipe_path) ) end it 'has the correct file metadata' do - expect(first_file_response_metadata).to include( - 'id' => global_id_of(first_file.conan_file_metadatum), - 'packageRevision' => first_file.conan_file_metadatum.package_revision, - 'conanPackageReference' => first_file.conan_file_metadatum.conan_package_reference, - 'recipeRevision' => first_file.conan_file_metadatum.recipe_revision, - 'conanFileType' => first_file.conan_file_metadatum.conan_file_type.upcase + expect(first_file_response_metadata).to match( + a_graphql_entity_for( + first_file.conan_file_metadatum, + :package_revision, :conan_package_reference, :recipe_revision, + conan_file_type: first_file.conan_file_metadatum.conan_file_type.upcase + ) ) end end diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb index d28d32b0df5..9d59a922660 100644 --- a/spec/requests/api/graphql/packages/maven_spec.rb +++ b/spec/requests/api/graphql/packages/maven_spec.rb @@ -11,12 +11,8 @@ RSpec.describe 'maven package details' do shared_examples 'correct maven metadata' do it 'has the correct metadata' do - expect(metadata_response).to include( - 'id' => global_id_of(package.maven_metadatum), - 'path' => package.maven_metadatum.path, - 'appGroup' => package.maven_metadatum.app_group, - 'appVersion' => package.maven_metadatum.app_version, - 'appName' => package.maven_metadatum.app_name + expect(metadata_response).to match a_graphql_entity_for( + package.maven_metadatum, :path, :app_group, :app_version, :app_name ) end end diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb index ba8d2ca42d2..87cffc67ce5 100644 --- a/spec/requests/api/graphql/packages/nuget_spec.rb +++ b/spec/requests/api/graphql/packages/nuget_spec.rb @@ -22,24 +22,19 @@ RSpec.describe 'nuget package details' do it_behaves_like 'a package with files' it 'has the correct metadata' do - expect(metadata_response).to include( - 'id' => global_id_of(package.nuget_metadatum), - 'licenseUrl' => package.nuget_metadatum.license_url, - 'projectUrl' => package.nuget_metadatum.project_url, - 'iconUrl' => package.nuget_metadatum.icon_url + expect(metadata_response).to match a_graphql_entity_for( + package.nuget_metadatum, :license_url, :project_url, :icon_url ) end it 'has dependency links' do - expect(dependency_link_response).to include( - 'id' => global_id_of(dependency_link), + expect(dependency_link_response).to match a_graphql_entity_for( + dependency_link, 'dependencyType' => dependency_link.dependency_type.upcase ) - expect(dependency_response).to include( - 'id' => global_id_of(dependency_link.dependency), - 'name' => dependency_link.dependency.name, - 'versionPattern' => dependency_link.dependency.version_pattern + expect(dependency_response).to match a_graphql_entity_for( + dependency_link.dependency, :name, :version_pattern ) end diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb index 365efc514d4..0335c1085b4 100644 --- a/spec/requests/api/graphql/packages/package_spec.rb +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -65,32 +65,6 @@ RSpec.describe 'package details' do end end - context 'there are other versions of this package' do - let(:depth) { 3 } - let(:excluded) { %w[metadata project tags pipelines] } # to limit the query complexity - - let_it_be(:siblings) { create_list(:composer_package, 2, project: project, name: composer_package.name) } - - it 'includes the sibling versions' do - subject - - expect(graphql_data_at(:package, :versions, :nodes)).to match_array( - siblings.map { |p| a_hash_including('id' => global_id_of(p)) } - ) - end - - context 'going deeper' do - let(:depth) { 6 } - - it 'does not create a cycle of versions' do - subject - - expect(graphql_data_at(:package, :versions, :nodes, :version)).to be_present - expect(graphql_data_at(:package, :versions, :nodes, :versions, :nodes)).to match_array [nil, nil] - end - end - end - context 'with package files pending destruction' do let_it_be(:package_file) { create(:package_file, package: composer_package) } let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: composer_package) } diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb index 64fe7d29a7a..0cc5bd2e3b2 100644 --- a/spec/requests/api/graphql/packages/pypi_spec.rb +++ b/spec/requests/api/graphql/packages/pypi_spec.rb @@ -19,9 +19,8 @@ RSpec.describe 'pypi package details' do it_behaves_like 'a package with files' it 'has the correct metadata' do - expect(metadata_response).to include( - 'id' => global_id_of(package.pypi_metadatum), - 'requiredPython' => package.pypi_metadatum.required_python + expect(metadata_response).to match a_graphql_entity_for( + package.pypi_metadatum, :required_python ) end end 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 1793d4961eb..773922c1864 100644 --- a/spec/requests/api/graphql/project/alert_management/integrations_spec.rb +++ b/spec/requests/api/graphql/project/alert_management/integrations_spec.rb @@ -53,33 +53,24 @@ RSpec.describe 'getting Alert Management Integrations' do end context 'when no extra params given' do - let(:http_integration_response) { integrations.first } - let(:prometheus_integration_response) { integrations.second } - it_behaves_like 'a working graphql query' - it { expect(integrations.size).to eq(2) } - it 'returns the correct properties of the integrations' do - expect(http_integration_response).to include( - 'id' => global_id_of(active_http_integration), - 'type' => 'HTTP', - 'name' => active_http_integration.name, - 'active' => active_http_integration.active, - 'token' => active_http_integration.token, - 'url' => active_http_integration.url, - 'apiUrl' => nil - ) - - expect(prometheus_integration_response).to include( - 'id' => global_id_of(prometheus_integration), - 'type' => 'PROMETHEUS', - 'name' => 'Prometheus', - 'active' => prometheus_integration.manual_configuration?, - 'token' => project_alerting_setting.token, - 'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json", - 'apiUrl' => prometheus_integration.api_url - ) + expect(integrations).to match [ + a_graphql_entity_for( + active_http_integration, + :name, :active, :token, :url, type: 'HTTP', api_url: nil + ), + a_graphql_entity_for( + prometheus_integration, + 'type' => 'PROMETHEUS', + 'name' => 'Prometheus', + 'active' => prometheus_integration.manual_configuration?, + 'token' => project_alerting_setting.token, + 'url' => "http://localhost/#{project.full_path}/prometheus/alerts/notify.json", + 'apiUrl' => prometheus_integration.api_url + ) + ] end end @@ -88,17 +79,9 @@ RSpec.describe 'getting Alert Management Integrations' do it_behaves_like 'a working graphql query' - it { expect(integrations).to be_one } - it 'returns the correct properties of the HTTP integration' do - expect(integrations.first).to include( - 'id' => global_id_of(active_http_integration), - 'type' => 'HTTP', - 'name' => active_http_integration.name, - 'active' => active_http_integration.active, - 'token' => active_http_integration.token, - 'url' => active_http_integration.url, - 'apiUrl' => nil + expect(integrations).to contain_exactly a_graphql_entity_for( + active_http_integration, :name, :active, :token, :url, type: 'HTTP', api_url: nil ) end end @@ -108,11 +91,9 @@ RSpec.describe 'getting Alert Management Integrations' do it_behaves_like 'a working graphql query' - it { expect(integrations).to be_one } - it 'returns the correct properties of the Prometheus Integration' do - expect(integrations.first).to include( - 'id' => global_id_of(prometheus_integration), + expect(integrations).to contain_exactly a_graphql_entity_for( + prometheus_integration, 'type' => 'PROMETHEUS', 'name' => 'Prometheus', 'active' => prometheus_integration.manual_configuration?, diff --git a/spec/requests/api/graphql/project/cluster_agents_spec.rb b/spec/requests/api/graphql/project/cluster_agents_spec.rb index c9900fea277..a34df0ee6f4 100644 --- a/spec/requests/api/graphql/project/cluster_agents_spec.rb +++ b/spec/requests/api/graphql/project/cluster_agents_spec.rb @@ -29,7 +29,7 @@ RSpec.describe 'Project.cluster_agents' do post_graphql(query, current_user: current_user) expect(graphql_data_at(:project, :cluster_agents, :nodes)).to match_array( - agents.map { |agent| a_hash_including('id' => global_id_of(agent)) } + agents.map { |agent| a_graphql_entity_for(agent) } ) end @@ -62,9 +62,9 @@ RSpec.describe 'Project.cluster_agents' do tokens = graphql_data_at(:project, :cluster_agents, :nodes, :tokens, :nodes) expect(tokens).to match([ - a_hash_including('id' => global_id_of(token_3)), - a_hash_including('id' => global_id_of(token_2)), - a_hash_including('id' => global_id_of(token_1)) + a_graphql_entity_for(token_3), + a_graphql_entity_for(token_2), + a_graphql_entity_for(token_1) ]) end 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 new file mode 100644 index 00000000000..708fa96986c --- /dev/null +++ b/spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting incident timeline events' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user) } + let_it_be(:updated_by_user) { create(:user) } + let_it_be(:incident) { create(:incident, project: project) } + let_it_be(:another_incident) { create(:incident, project: project) } + let_it_be(:promoted_from_note) { create(:note, project: project, noteable: incident) } + + let_it_be(:timeline_event) do + create( + :incident_management_timeline_event, + incident: incident, + project: project, + updated_by_user: updated_by_user, + promoted_from_note: promoted_from_note + ) + end + + let_it_be(:second_timeline_event) do + create(:incident_management_timeline_event, incident: incident, project: project) + end + + let_it_be(:another_timeline_event) do + create(:incident_management_timeline_event, incident: another_incident, project: project) + end + + let(:params) { { incident_id: incident.to_global_id.to_s } } + + let(:timeline_event_fields) do + <<~QUERY + nodes { + id + author { id username } + updatedByUser { id username } + incident { id title } + note + noteHtml + promotedFromNote { id body } + editable + action + occurredAt + createdAt + updatedAt + } + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('incidentManagementTimelineEvents', params, timeline_event_fields) + ) + end + + let(:timeline_events) do + graphql_data.dig('project', 'incidentManagementTimelineEvents', 'nodes') + end + + before do + project.add_guest(current_user) + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it 'returns the correct number of timeline events' do + expect(timeline_events.count).to eq(2) + end + + it 'returns the correct properties of the incident timeline events' do + expect(timeline_events.first).to include( + 'author' => { + 'id' => timeline_event.author.to_global_id.to_s, + 'username' => timeline_event.author.username + }, + 'updatedByUser' => { + 'id' => updated_by_user.to_global_id.to_s, + 'username' => updated_by_user.username + }, + 'incident' => { + 'id' => incident.to_global_id.to_s, + 'title' => incident.title + }, + 'note' => timeline_event.note, + 'noteHtml' => timeline_event.note_html, + 'promotedFromNote' => { + 'id' => promoted_from_note.to_global_id.to_s, + 'body' => promoted_from_note.note + }, + 'editable' => false, + 'action' => timeline_event.action, + 'occurredAt' => timeline_event.occurred_at.iso8601, + 'createdAt' => timeline_event.created_at.iso8601, + 'updatedAt' => timeline_event.updated_at.iso8601 + ) + end + + context 'when filtering by id' do + let(:params) { { incident_id: incident.to_global_id.to_s, id: timeline_event.to_global_id.to_s } } + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('incidentManagementTimelineEvent', params, 'id occurredAt') + ) + end + + it_behaves_like 'a working graphql query' + + it 'returns a single timeline event', :aggregate_failures do + single_timeline_event = graphql_data.dig('project', 'incidentManagementTimelineEvent') + + expect(single_timeline_event).to include( + 'id' => timeline_event.to_global_id.to_s, + 'occurredAt' => timeline_event.occurred_at.iso8601 + ) + end + end +end 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 f544d78ecbb..8cda61f0628 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 @@ -71,11 +71,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) it 'finds all the designs as of the given version' do post_query - expect(data).to match( - a_hash_including( - 'id' => global_id_of(design_at_version), - 'filename' => design.filename - )) + expect(data).to match a_graphql_entity_for(design_at_version, filename: design.filename) end context 'when the current_user is not authorized' do @@ -119,7 +115,8 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) let(:results) do issue.designs.visible_at_version(version).map do |d| dav = build(:design_at_version, design: d, version: version) - { 'id' => global_id_of(dav), 'filename' => d.filename } + + a_graphql_entity_for(dav, filename: d.filename) end end @@ -132,8 +129,8 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) describe 'filtering' do let(:designs) { issue.designs.sample(3) } let(:filenames) { designs.map(&:filename) } - let(:ids) do - designs.map { |d| global_id_of(build(:design_at_version, design: d, version: version)) } + let(:expected_designs) do + designs.map { |d| a_graphql_entity_for(build(:design_at_version, design: d, version: version)) } end before do @@ -144,7 +141,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) let(:dav_params) { { filenames: filenames } } it 'finds the designs by filename' do - expect(data.map { |e| e.dig('node', 'id') }).to match_array(ids) + expect(data.map { |e| e['node'] }).to match_array expected_designs end end @@ -160,9 +157,9 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) describe 'pagination' do let(:end_cursor) { graphql_data_at(*path_prefix, :designs_at_version, :page_info, :end_cursor) } - let(:ids) do + let(:entities) do ::DesignManagement::Design.visible_at_version(version).order(:id).map do |d| - global_id_of(build(:design_at_version, design: d, version: version)) + a_graphql_entity_for(build(:design_at_version, design: d, version: version)) end end @@ -178,19 +175,19 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) let(:fields) { ['pageInfo { endCursor }', 'edges { node { id } }'] } def response_values(data = graphql_data) - data.dig(*path).map { |e| e.dig('node', 'id') } + data.dig(*path).map { |e| e['node'] } end it 'sorts designs for reliable pagination' do post_graphql(query, current_user: current_user) - expect(response_values).to match_array(ids.take(2)) + expect(response_values).to match_array(entities.take(2)) post_graphql(cursored_query, current_user: current_user) new_data = Gitlab::Json.parse(response.body).fetch('data') - expect(response_values(new_data)).to match_array(ids.drop(2)) + expect(response_values(new_data)).to match_array(entities.drop(2)) end end end @@ -202,9 +199,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha) end let(:results) do - version.designs.map do |design| - { 'id' => global_id_of(design), 'filename' => design.filename } - end + version.designs.map { |design| a_graphql_entity_for(design, :filename) } end it 'finds all the designs as of the given version' do 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 f0205319983..02bc9457c07 100644 --- a/spec/requests/api/graphql/project/issue/designs/designs_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/designs_spec.rb @@ -58,8 +58,8 @@ RSpec.describe 'Getting designs related to an issue' do post_graphql(query, current_user: current_user) - expect(design_response).to eq( - 'id' => design.to_global_id.to_s, + expect(design_response).to match a_graphql_entity_for( + design, 'event' => 'CREATION', 'fullPath' => design.full_path, 'filename' => design.filename, @@ -93,7 +93,7 @@ RSpec.describe 'Getting designs related to an issue' do let(:end_cursor) { design_collection.dig('designs', 'pageInfo', 'endCursor') } - let(:ids) { issue.designs.order(:id).map { |d| global_id_of(d) } } + let(:expected_designs) { issue.designs.order(:id).map { |d| a_graphql_entity_for(d) } } let(:query) { make_query(designs_fragment(first: 2)) } @@ -107,19 +107,19 @@ RSpec.describe 'Getting designs related to an issue' do query_graphql_field(:designs, params, design_query_fields) end - def response_ids(data = graphql_data) + def response_designs(data = graphql_data) path = %w[project issue designCollection designs edges] - data.dig(*path).map { |e| e.dig('node', 'id') } + data.dig(*path).map { |e| e['node'] } end it 'sorts designs for reliable pagination' do - expect(response_ids).to match_array(ids.take(2)) + expect(response_designs).to match_array(expected_designs.take(2)) post_graphql(cursored_query, current_user: current_user) new_data = Gitlab::Json.parse(response.body).fetch('data') - expect(response_ids(new_data)).to match_array(ids.drop(2)) + expect(response_designs(new_data)).to match_array(expected_designs.drop(2)) end end @@ -273,8 +273,10 @@ RSpec.describe 'Getting designs related to an issue' do end it 'returns the correct v432x230-sized design images' do + v0 = design.actions.most_recent.first.version + expect(design_nodes).to contain_exactly( - a_hash_including('imageV432x230' => design_image_url(design, ref: version.sha, size: :v432x230)), + a_hash_including('imageV432x230' => design_image_url(design, ref: v0.sha, size: :v432x230)), a_hash_including('imageV432x230' => design_image_url(second_design, ref: version.sha, size: :v432x230)) ) end @@ -323,8 +325,10 @@ RSpec.describe 'Getting designs related to an issue' do end it 'returns the correct v432x230-sized design images' do + v0 = design.actions.most_recent.first.version + expect(design_nodes).to contain_exactly( - a_hash_including('imageV432x230' => design_image_url(design, ref: version.sha, size: :v432x230)), + a_hash_including('imageV432x230' => design_image_url(design, ref: v0.sha, size: :v432x230)), a_hash_including('imageV432x230' => design_image_url(second_design, ref: version.sha, size: :v432x230)) ) end 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 de2ace95757..3b1eb0b4b02 100644 --- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb @@ -51,7 +51,7 @@ RSpec.describe 'Getting designs related to an issue' do design_data = designs_data['nodes'].first note_data = design_data['notes']['nodes'].first - expect(note_data['id']).to eq(note.to_global_id.to_s) + expect(note_data).to match(a_graphql_entity_for(note)) end def query(note_fields = all_graphql_fields_for(Note, max_depth: 1)) diff --git a/spec/requests/api/graphql/project/issue_spec.rb b/spec/requests/api/graphql/project/issue_spec.rb index ddf63a8f2c9..2415e9ef60f 100644 --- a/spec/requests/api/graphql/project/issue_spec.rb +++ b/spec/requests/api/graphql/project/issue_spec.rb @@ -144,10 +144,7 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do data = graphql_data.dig(*path) - expect(data).to match( - a_hash_including('id' => global_id_of(version), - 'sha' => version.sha) - ) + expect(data).to match a_graphql_entity_for(version, :sha) end end @@ -184,6 +181,6 @@ RSpec.describe 'Query.project(fullPath).issue(iid)' do end def id_hash(object) - a_hash_including('id' => global_id_of(object)) + a_graphql_entity_for(object) end end diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index cefe88aafc8..d2f34080be3 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -66,7 +66,7 @@ RSpec.describe 'getting merge request information nested in a project' do it 'includes reviewers' do expected = merge_request.reviewers.map do |r| - a_hash_including('id' => global_id_of(r), 'username' => r.username) + a_graphql_entity_for(r, :username) end post_graphql(query, current_user: current_user) @@ -425,7 +425,7 @@ RSpec.describe 'getting merge request information nested in a project' do other_users.each do |user| assign_user(user) - merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user) + merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user, state: :attention_requested) end expect { post_graphql(query) }.not_to exceed_query_limit(baseline) @@ -466,7 +466,7 @@ RSpec.describe 'getting merge request information nested in a project' do let(:can_update) { false } def assign_user(user) - merge_request.merge_request_reviewers.create!(reviewer: user) + merge_request.merge_request_reviewers.create!(reviewer: user, state: :attention_requested) end end diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index 303748bc70e..5daec5543c0 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'getting merge request listings nested in a project' do let_it_be(:current_user) { create(:user) } let_it_be(:label) { create(:label, project: project) } - let_it_be(:merge_request_a) do + let_it_be_with_reload(:merge_request_a) do create(:labeled_merge_request, :unique_branches, source_project: project, labels: [label]) end @@ -96,7 +96,7 @@ RSpec.describe 'getting merge request listings nested in a project' do where(:field, :subfield, :is_connection) do nested_fields_of('MergeRequest').flat_map do |name, field| type = field_type(field) - is_connection = type.name.ends_with?('Connection') + is_connection = type.graphql_name.ends_with?('Connection') type = field_type(type.fields['nodes']) if is_connection type.fields @@ -412,6 +412,10 @@ RSpec.describe 'getting merge request listings nested in a project' do describe 'sorting and pagination' do let(:data_path) { [:project, :mergeRequests] } + def pagination_results_data(nodes) + nodes + end + def pagination_query(params) graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY) mergeRequests(#{params}) { @@ -429,7 +433,7 @@ RSpec.describe 'getting merge request listings nested in a project' do merge_request_c, merge_request_e, merge_request_a - ].map { |mr| global_id_of(mr) } + ].map { |mr| a_graphql_entity_for(mr) } end before do @@ -455,7 +459,7 @@ RSpec.describe 'getting merge request listings nested in a project' do query = pagination_query(params) post_graphql(query, current_user: current_user) - expect(results.map { |item| item["id"] }).to eq(all_records.last(2)) + expect(results).to match(all_records.last(2)) end end end @@ -469,7 +473,7 @@ RSpec.describe 'getting merge request listings nested in a project' do merge_request_c, merge_request_e, merge_request_a - ].map { |mr| global_id_of(mr) } + ].map { |mr| a_graphql_entity_for(mr) } end before do @@ -495,17 +499,19 @@ RSpec.describe 'getting merge request listings nested in a project' do query = pagination_query(params) post_graphql(query, current_user: current_user) - expect(results.map { |item| item["id"] }).to eq(all_records.last(2)) + expect(results).to match(all_records.last(2)) end end end end context 'when only the count is requested' do + let_it_be(:merged_at) { Time.new(2020, 1, 3) } + context 'when merged at filter is present' do let_it_be(:merge_request) do create(:merge_request, :unique_branches, source_project: project).tap do |mr| - mr.metrics.update!(merged_at: Time.new(2020, 1, 3)) + mr.metrics.update!(merged_at: merged_at, created_at: merged_at - 2.days) end end @@ -522,12 +528,18 @@ RSpec.describe 'getting merge request listings nested in a project' do it 'does not query the merge requests table for the count' do query_recorder = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - queries = query_recorder.data.each_value.first[:occurrences] + queries = query_recorder.log expect(queries).not_to include(match(/SELECT COUNT\(\*\) FROM "merge_requests"/)) expect(queries).to include(match(/SELECT COUNT\(\*\) FROM "merge_request_metrics"/)) end context 'when total_time_to_merge and count is queried' do + let_it_be(:merge_request_2) do + create(:merge_request, :unique_branches, source_project: project).tap do |mr| + mr.metrics.update!(merged_at: merged_at, created_at: merged_at - 1.day) + end + end + let(:query) do graphql_query_for(:project, { full_path: project.full_path }, <<~QUERY) mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0) { @@ -537,11 +549,18 @@ RSpec.describe 'getting merge request listings nested in a project' do QUERY end - it 'does not query the merge requests table for the total_time_to_merge' do + it 'uses the merge_request_metrics table for total_time_to_merge' do query_recorder = ActiveRecord::QueryRecorder.new { post_graphql(query, current_user: current_user) } - queries = query_recorder.data.each_value.first[:occurrences] - expect(queries).to include(match(/SELECT.+SUM.+FROM "merge_request_metrics" WHERE/)) + expect(query_recorder.log).to include(match(/SELECT.+SUM.+FROM "merge_request_metrics" WHERE/)) + end + + it 'returns the correct total time to merge' do + post_graphql(query, current_user: current_user) + + sum = graphql_data_at(:project, :merge_requests, :total_time_to_merge) + + expect(sum).to eq(3.days.to_f) end end diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb index 2fede4c7285..3e8948d83b1 100644 --- a/spec/requests/api/graphql/project/milestones_spec.rb +++ b/spec/requests/api/graphql/project/milestones_spec.rb @@ -33,7 +33,7 @@ RSpec.describe 'getting milestone listings nested in a project' do def result_list(expected) expected.map do |milestone| - a_hash_including('id' => global_id_of(milestone)) + a_graphql_entity_for(milestone) end end diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb index 73e02e2a4b1..ccf97918021 100644 --- a/spec/requests/api/graphql/project/pipeline_spec.rb +++ b/spec/requests/api/graphql/project/pipeline_spec.rb @@ -89,17 +89,16 @@ RSpec.describe 'getting pipeline information nested in a project' do post_graphql(query, current_user: current_user) expect(graphql_data_at(*path, :jobs, :nodes)).to contain_exactly( - a_hash_including( - 'name' => build_job.name, - 'status' => build_job.status.upcase, - 'duration' => build_job.duration + a_graphql_entity_for( + build_job, :name, :duration, + 'status' => build_job.status.upcase ), - a_hash_including( - 'id' => global_id_of(failed_build), + a_graphql_entity_for( + failed_build, 'status' => failed_build.status.upcase ), - a_hash_including( - 'id' => global_id_of(bridge), + a_graphql_entity_for( + bridge, 'status' => bridge.status.upcase ) ) @@ -135,7 +134,7 @@ RSpec.describe 'getting pipeline information nested in a project' do post_graphql(query, current_user: current_user, variables: variables) expect(graphql_data_at(*path, :jobs, :nodes)) - .to contain_exactly(a_hash_including('id' => global_id_of(failed_build))) + .to contain_exactly(a_graphql_entity_for(failed_build)) end end @@ -166,7 +165,7 @@ RSpec.describe 'getting pipeline information nested in a project' do end let(:the_job) do - a_hash_including('name' => build_job.name, 'id' => global_id_of(build_job)) + a_graphql_entity_for(build_job, :name) end it 'can request a build by name' do diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb index 315d44884ff..c3281b44954 100644 --- a/spec/requests/api/graphql/project/project_members_spec.rb +++ b/spec/requests/api/graphql/project/project_members_spec.rb @@ -60,7 +60,10 @@ RSpec.describe 'getting project members information' do fetch_members(project: parent_project, args: { relations: [:DIRECT] }) expect(graphql_errors).to be_nil - expect(graphql_data_at(:project, :project_members, :edges, :node)).to contain_exactly({ 'user' => { 'id' => global_id_of(user) } }, 'user' => nil) + expect(graphql_data_at(:project, :project_members, :edges, :node)).to contain_exactly( + a_graphql_entity_for(user: a_graphql_entity_for(user)), + { 'user' => nil } + ) end end @@ -238,7 +241,7 @@ RSpec.describe 'getting project members information' do def expect_array_response(*items) expect(response).to have_gitlab_http_status(:success) - member_gids = graphql_data_at(:project, :project_members, :edges, :node, :user, :id) - expect(member_gids).to match_array(items.map { |u| global_id_of(u) }) + members = graphql_data_at(:project, :project_members, :edges, :node, :user) + expect(members).to match_array(items.map { |u| a_graphql_entity_for(u) }) end end diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index 77abac4ef04..c4899dbb71e 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -77,10 +77,10 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do post_query expected = release.milestones.order_by_dates_and_title.map do |milestone| - { 'id' => global_id_of(milestone), 'title' => milestone.title } + a_graphql_entity_for(milestone, :title) end - expect(data).to eq(expected) + expect(data).to match(expected) end end @@ -94,10 +94,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do it 'finds the author of the release' do post_query - expect(data).to eq( - 'id' => global_id_of(release.author), - 'username' => release.author.username - ) + expect(data).to match a_graphql_entity_for(release.author, :username) end end @@ -142,13 +139,11 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do post_query expected = release.links.map do |link| - { - 'id' => global_id_of(link), - 'name' => link.name, - 'url' => link.url, + a_graphql_entity_for( + link, :name, :url, 'external' => link.external?, 'directAssetUrl' => link.filepath ? Gitlab::Routing.url_helpers.project_release_url(project, release) << "/downloads#{link.filepath}" : link.url - } + ) end expect(data).to match_array(expected) @@ -218,10 +213,8 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do evidence = release.evidences.first.present - expect(data["nodes"].first).to eq( - 'id' => global_id_of(evidence), - 'sha' => evidence.sha, - 'filepath' => evidence.filepath, + expect(data["nodes"].first).to match a_graphql_entity_for( + evidence, :sha, :filepath, 'collectedAt' => evidence.collected_at.utc.iso8601 ) end @@ -274,10 +267,10 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do post_query expected = release.milestones.order_by_dates_and_title.map do |milestone| - { 'id' => global_id_of(milestone), 'title' => milestone.title } + a_graphql_entity_for(milestone, :title) end - expect(data).to eq(expected) + expect(data).to match(expected) end end @@ -291,10 +284,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do it 'finds the author of the release' do post_query - expect(data).to eq( - 'id' => global_id_of(release.author), - 'username' => release.author.username - ) + expect(data).to match a_graphql_entity_for(release.author, :username) end end @@ -339,13 +329,11 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do post_query expected = release.links.map do |link| - { - 'id' => global_id_of(link), - 'name' => link.name, - 'url' => link.url, + a_graphql_entity_for( + link, :name, :url, 'external' => true, 'directAssetUrl' => link.filepath ? Gitlab::Routing.url_helpers.project_release_url(project, release) << "/downloads#{link.filepath}" : link.url - } + ) end expect(data).to match_array(expected) diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb index 9f1d9ab204a..8f2d2cffef2 100644 --- a/spec/requests/api/graphql/project/terraform/state_spec.rb +++ b/spec/requests/api/graphql/project/terraform/state_spec.rb @@ -57,22 +57,22 @@ RSpec.describe 'query a single terraform state' do it_behaves_like 'a working graphql query' it 'returns terraform state data' do - expect(data).to match(a_hash_including({ - 'id' => global_id_of(terraform_state), - 'name' => terraform_state.name, + expect(data).to match a_graphql_entity_for( + terraform_state, + :name, 'lockedAt' => terraform_state.locked_at.iso8601, 'createdAt' => terraform_state.created_at.iso8601, 'updatedAt' => terraform_state.updated_at.iso8601, - 'lockedByUser' => { 'id' => global_id_of(terraform_state.locked_by_user) }, - 'latestVersion' => { - 'id' => eq(global_id_of(latest_version)), + 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), + 'latestVersion' => a_graphql_entity_for( + latest_version, 'serial' => eq(latest_version.version), 'createdAt' => eq(latest_version.created_at.iso8601), 'updatedAt' => eq(latest_version.updated_at.iso8601), - 'createdByUser' => { 'id' => eq(global_id_of(latest_version.created_by_user)) }, + 'createdByUser' => a_graphql_entity_for(latest_version.created_by_user), 'job' => { 'name' => eq(latest_version.build.name) } - } - })) + ) + ) end context 'unauthorized users' do diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb index 2879530acc5..a7ec6f69776 100644 --- a/spec/requests/api/graphql/project/terraform/states_spec.rb +++ b/spec/requests/api/graphql/project/terraform/states_spec.rb @@ -62,23 +62,22 @@ RSpec.describe 'query terraform states' do ) ) - expect(data['nodes']).to contain_exactly({ - 'id' => global_id_of(terraform_state), - 'name' => terraform_state.name, + expect(data['nodes']).to contain_exactly a_graphql_entity_for( + terraform_state, :name, 'lockedAt' => terraform_state.locked_at.iso8601, 'createdAt' => terraform_state.created_at.iso8601, 'updatedAt' => terraform_state.updated_at.iso8601, - 'lockedByUser' => { 'id' => global_id_of(terraform_state.locked_by_user) }, - 'latestVersion' => { - 'id' => eq(global_id_of(latest_version)), + 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user), + 'latestVersion' => a_graphql_entity_for( + latest_version, 'serial' => eq(latest_version.version), 'downloadPath' => eq(download_path), 'createdAt' => eq(latest_version.created_at.iso8601), 'updatedAt' => eq(latest_version.updated_at.iso8601), - 'createdByUser' => { 'id' => eq(global_id_of(latest_version.created_by_user)) }, + 'createdByUser' => a_graphql_entity_for(latest_version.created_by_user), 'job' => { 'name' => eq(latest_version.build.name) } - } - }) + ) + ) end it 'returns count of terraform states' do diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb index d650acc8354..4aa9c4b8254 100644 --- a/spec/requests/api/graphql/query_spec.rb +++ b/spec/requests/api/graphql/query_spec.rb @@ -76,10 +76,8 @@ RSpec.describe 'Query' do it_behaves_like 'a working graphql query' it_behaves_like 'a query that needs authorization' - context 'the current user is able to read designs' do - it 'fetches the expected data' do - expect(query_result).to eq('id' => global_id_of(version), 'sha' => version.sha) - end + it 'fetches the expected data' do + expect(query_result).to match a_graphql_entity_for(version, :sha) end end @@ -106,13 +104,13 @@ RSpec.describe 'Query' do context 'the current user is able to read designs' do it 'fetches the expected data, including the correct associations' do - expect(query_result).to eq( - 'id' => global_id_of(design_at_version), + expect(query_result).to match a_graphql_entity_for( + design_at_version, 'filename' => design_at_version.design.filename, - 'version' => { 'id' => global_id_of(version), 'sha' => version.sha }, - 'design' => { 'id' => global_id_of(design) }, + 'version' => a_graphql_entity_for(version, :sha), + 'design' => a_graphql_entity_for(design), 'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s }, - 'project' => { 'id' => global_id_of(project), 'fullPath' => project.full_path } + 'project' => a_graphql_entity_for(project, :full_path) ) end end 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 a8c087d1fbf..37a85b98e5f 100644 --- a/spec/requests/api/graphql/user/starred_projects_query_spec.rb +++ b/spec/requests/api/graphql/user/starred_projects_query_spec.rb @@ -42,7 +42,7 @@ RSpec.describe 'Getting starredProjects of the user' do it 'found only public project' do expect(starred_projects).to contain_exactly( - a_hash_including('id' => global_id_of(project_a)) + a_graphql_entity_for(project_a) ) end @@ -51,9 +51,9 @@ RSpec.describe 'Getting starredProjects of the user' do it 'found all projects' do expect(starred_projects).to contain_exactly( - a_hash_including('id' => global_id_of(project_a)), - a_hash_including('id' => global_id_of(project_b)), - a_hash_including('id' => global_id_of(project_c)) + a_graphql_entity_for(project_a), + a_graphql_entity_for(project_b), + a_graphql_entity_for(project_c) ) end end @@ -69,8 +69,8 @@ RSpec.describe 'Getting starredProjects of the user' do it 'finds public and member projects' do expect(starred_projects).to contain_exactly( - a_hash_including('id' => global_id_of(project_a)), - a_hash_including('id' => global_id_of(project_b)) + a_graphql_entity_for(project_a), + a_graphql_entity_for(project_b) ) end end @@ -93,9 +93,9 @@ RSpec.describe 'Getting starredProjects of the user' do it 'finds all projects starred by the user, which the current user has access to' do expect(starred_projects).to contain_exactly( - a_hash_including('id' => global_id_of(project_a)), - a_hash_including('id' => global_id_of(project_b)), - a_hash_including('id' => global_id_of(project_c)) + a_graphql_entity_for(project_a), + a_graphql_entity_for(project_b), + a_graphql_entity_for(project_c) ) end end diff --git a/spec/requests/api/graphql/user_query_spec.rb b/spec/requests/api/graphql/user_query_spec.rb index 1cba3674d25..8f286180617 100644 --- a/spec/requests/api/graphql/user_query_spec.rb +++ b/spec/requests/api/graphql/user_query_spec.rb @@ -91,11 +91,11 @@ RSpec.describe 'getting user information' do presenter = UserPresenter.new(user) expect(graphql_data['user']).to match( - a_hash_including( - 'id' => global_id_of(user), + a_graphql_entity_for( + user, + :username, 'state' => presenter.state, 'name' => presenter.name, - 'username' => presenter.username, 'webUrl' => presenter.web_url, 'avatarUrl' => presenter.avatar_url, 'email' => presenter.public_email, @@ -121,9 +121,9 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr)), - a_hash_including('id' => global_id_of(assigned_mr_b)), - a_hash_including('id' => global_id_of(assigned_mr_c)) + a_graphql_entity_for(assigned_mr), + a_graphql_entity_for(assigned_mr_b), + a_graphql_entity_for(assigned_mr_c) ) end @@ -145,7 +145,7 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr_b)) + a_graphql_entity_for(assigned_mr_b) ) end end @@ -157,8 +157,8 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr_b)), - a_hash_including('id' => global_id_of(assigned_mr_c)) + a_graphql_entity_for(assigned_mr_b), + a_graphql_entity_for(assigned_mr_c) ) end end @@ -169,7 +169,7 @@ RSpec.describe 'getting user information' do it 'finds the authored mrs' do expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr_b)) + a_graphql_entity_for(assigned_mr_b) ) end end @@ -185,8 +185,8 @@ RSpec.describe 'getting user information' do post_graphql(query, current_user: current_user) expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr_b)), - a_hash_including('id' => global_id_of(assigned_mr_c)) + a_graphql_entity_for(assigned_mr_b), + a_graphql_entity_for(assigned_mr_c) ) end end @@ -212,9 +212,9 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(reviewed_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(reviewed_mr)), - a_hash_including('id' => global_id_of(reviewed_mr_b)), - a_hash_including('id' => global_id_of(reviewed_mr_c)) + a_graphql_entity_for(reviewed_mr), + a_graphql_entity_for(reviewed_mr_b), + a_graphql_entity_for(reviewed_mr_c) ) end @@ -236,7 +236,7 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(reviewed_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(reviewed_mr_b)) + a_graphql_entity_for(reviewed_mr_b) ) end end @@ -248,8 +248,8 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(reviewed_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(reviewed_mr_b)), - a_hash_including('id' => global_id_of(reviewed_mr_c)) + a_graphql_entity_for(reviewed_mr_b), + a_graphql_entity_for(reviewed_mr_c) ) end end @@ -260,7 +260,7 @@ RSpec.describe 'getting user information' do it 'finds the authored mrs' do expect(reviewed_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(reviewed_mr_b)) + a_graphql_entity_for(reviewed_mr_b) ) end end @@ -275,7 +275,7 @@ RSpec.describe 'getting user information' do post_graphql(query, current_user: current_user) expect(reviewed_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(reviewed_mr_c)) + a_graphql_entity_for(reviewed_mr_c) ) end end @@ -301,9 +301,9 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(authored_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(authored_mr)), - a_hash_including('id' => global_id_of(authored_mr_b)), - a_hash_including('id' => global_id_of(authored_mr_c)) + a_graphql_entity_for(authored_mr), + a_graphql_entity_for(authored_mr_b), + a_graphql_entity_for(authored_mr_c) ) end @@ -329,8 +329,8 @@ RSpec.describe 'getting user information' do post_graphql(query, current_user: current_user) expect(authored_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(authored_mr)), - a_hash_including('id' => global_id_of(authored_mr_c)) + a_graphql_entity_for(authored_mr), + a_graphql_entity_for(authored_mr_c) ) end end @@ -346,8 +346,8 @@ RSpec.describe 'getting user information' do post_graphql(query, current_user: current_user) expect(authored_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(authored_mr_b)), - a_hash_including('id' => global_id_of(authored_mr_c)) + a_graphql_entity_for(authored_mr_b), + a_graphql_entity_for(authored_mr_c) ) end end @@ -359,7 +359,7 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(authored_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(authored_mr_b)) + a_graphql_entity_for(authored_mr_b) ) end end @@ -371,8 +371,8 @@ RSpec.describe 'getting user information' do it 'selects the correct MRs' do expect(authored_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(authored_mr_b)), - a_hash_including('id' => global_id_of(authored_mr_c)) + a_graphql_entity_for(authored_mr_b), + a_graphql_entity_for(authored_mr_c) ) end end @@ -417,7 +417,7 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(group_memberships).to include( - a_hash_including('id' => global_id_of(membership_a)) + a_graphql_entity_for(membership_a) ) end end @@ -440,7 +440,7 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(project_memberships).to include( - a_hash_including('id' => global_id_of(membership_a)) + a_graphql_entity_for(membership_a) ) end end @@ -460,7 +460,7 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(authored_mrs).to include( - a_hash_including('id' => global_id_of(authored_mr)) + a_graphql_entity_for(authored_mr) ) end end @@ -480,9 +480,9 @@ RSpec.describe 'getting user information' do it 'can be found' do expect(assigned_mrs).to contain_exactly( - a_hash_including('id' => global_id_of(assigned_mr)), - a_hash_including('id' => global_id_of(assigned_mr_b)), - a_hash_including('id' => global_id_of(assigned_mr_c)) + a_graphql_entity_for(assigned_mr), + a_graphql_entity_for(assigned_mr_b), + a_graphql_entity_for(assigned_mr_c) ) end end diff --git a/spec/requests/api/graphql/users_spec.rb b/spec/requests/api/graphql/users_spec.rb index fe824834a2c..79ee3c2cb57 100644 --- a/spec/requests/api/graphql/users_spec.rb +++ b/spec/requests/api/graphql/users_spec.rb @@ -72,12 +72,12 @@ RSpec.describe 'Users' do post_query expect(graphql_data.dig('users', 'nodes')).to include( - { "id" => user0.to_global_id.to_s }, - { "id" => user1.to_global_id.to_s }, - { "id" => user2.to_global_id.to_s }, - { "id" => user3.to_global_id.to_s }, - { "id" => admin.to_global_id.to_s }, - { "id" => another_admin.to_global_id.to_s } + a_graphql_entity_for(user0), + a_graphql_entity_for(user1), + a_graphql_entity_for(user2), + a_graphql_entity_for(user3), + a_graphql_entity_for(admin), + a_graphql_entity_for(another_admin) ) end end @@ -91,15 +91,15 @@ RSpec.describe 'Users' do post_graphql(query, current_user: current_user) expect(graphql_data.dig('users', 'nodes')).to include( - { "id" => another_admin.to_global_id.to_s }, - { "id" => admin.to_global_id.to_s } + a_graphql_entity_for(another_admin), + a_graphql_entity_for(admin) ) expect(graphql_data.dig('users', 'nodes')).not_to include( - { "id" => user0.to_global_id.to_s }, - { "id" => user1.to_global_id.to_s }, - { "id" => user2.to_global_id.to_s }, - { "id" => user3.to_global_id.to_s } + a_graphql_entity_for(user0), + a_graphql_entity_for(user1), + a_graphql_entity_for(user2), + a_graphql_entity_for(user3) ) end end @@ -114,7 +114,7 @@ RSpec.describe 'Users' do end context 'when sorting by created_at' do - let_it_be(:ascending_users) { [user3, user2, user1, user0].map { |u| global_id_of(u) } } + let_it_be(:ascending_users) { [user3, user2, user1, user0].map { |u| global_id_of(u).to_s } } context 'when ascending' do it_behaves_like 'sorted paginated query' do diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb index bc5a8b3e006..5b34c21989a 100644 --- a/spec/requests/api/graphql/work_item_spec.rb +++ b/spec/requests/api/graphql/work_item_spec.rb @@ -33,7 +33,8 @@ RSpec.describe 'Query.work_item(id)' do 'lockVersion' => work_item.lock_version, 'state' => "OPEN", 'title' => work_item.title, - 'workItemType' => hash_including('id' => work_item.work_item_type.to_gid.to_s) + 'workItemType' => hash_including('id' => work_item.work_item_type.to_gid.to_s), + 'userPermissions' => { 'readWorkItem' => true, 'updateWorkItem' => true, 'deleteWorkItem' => false } ) end |