diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 18:25:58 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-06-16 18:25:58 +0000 |
commit | a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4 (patch) | |
tree | fb69158581673816a8cd895f9d352dcb3c678b1e /spec/requests/api/graphql | |
parent | d16b2e8639e99961de6ddc93909f3bb5c1445ba1 (diff) | |
download | gitlab-ce-a5f4bba440d7f9ea47046a0a561d49adf0a1e6d4.tar.gz |
Add latest changes from gitlab-org/gitlab@14-0-stable-eev14.0.0-rc42
Diffstat (limited to 'spec/requests/api/graphql')
20 files changed, 389 insertions, 284 deletions
diff --git a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb index 99647d0fa3a..578a71a7272 100644 --- a/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb +++ b/spec/requests/api/graphql/ci/ci_cd_setting_spec.rb @@ -47,6 +47,7 @@ RSpec.describe 'Getting Ci Cd Setting' do expect(settings_data['mergePipelinesEnabled']).to eql project.ci_cd_settings.merge_pipelines_enabled? expect(settings_data['mergeTrainsEnabled']).to eql project.ci_cd_settings.merge_trains_enabled? expect(settings_data['keepLatestArtifact']).to eql project.keep_latest_artifacts_available? + expect(settings_data['jobTokenScopeEnabled']).to eql project.ci_cd_settings.job_token_scope_enabled? end 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 452610ab18f..31cb0393d7f 100644 --- a/spec/requests/api/graphql/group/group_members_spec.rb +++ b/spec/requests/api/graphql/group/group_members_spec.rb @@ -14,6 +14,23 @@ RSpec.describe 'getting group members information' do [user_1, user_2].each { |user| parent_group.add_guest(user) } end + context 'when a member is invited only via email' do + before do + create(:group_member, :invited, source: parent_group) + end + + it 'returns null in the user field' do + fetch_members(group: parent_group, args: { relations: [:DIRECT] }) + + 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' => nil + ) + end + end + context 'when the request is correct' do it_behaves_like 'a working graphql query' do before do diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb index 601cab6aade..2b80b5239c8 100644 --- a/spec/requests/api/graphql/group/milestones_spec.rb +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -40,6 +40,13 @@ RSpec.describe 'Milestones through GroupQuery' do expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s) end + + it 'fetches milestones between timeframe start and end arguments' do + today = Date.today + fetch_milestones(user, { timeframe: { start: today.to_s, end: (today + 2.days).to_s } }) + + expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s) + end end context 'when filtering by state' do diff --git a/spec/requests/api/graphql/group/timelogs_spec.rb b/spec/requests/api/graphql/group/timelogs_spec.rb index 6e21a73afa9..05b6ee3ff89 100644 --- a/spec/requests/api/graphql/group/timelogs_spec.rb +++ b/spec/requests/api/graphql/group/timelogs_spec.rb @@ -17,8 +17,37 @@ RSpec.describe 'Timelogs through GroupQuery' do let(:timelogs_data) { graphql_data['group']['timelogs']['nodes'] } - before do - group.add_developer(user) + context 'when the project is private' do + let_it_be(:group2) { create(:group) } + let_it_be(:project2) { create(:project, :private, group: group2) } + let_it_be(:issue2) { create(:issue, project: project2) } + let_it_be(:timelog3) { create(:timelog, issue: issue2, spent_at: '2019-08-13 14:00:00') } + + subject { post_graphql(query(full_path: group2.full_path), current_user: user) } + + context 'when the user is not a member of the project' do + it 'returns no timelogs' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(graphql_errors).to be_nil + expect(timelog_array.size).to eq 0 + end + end + + context 'when the user is a member of the project' do + before do + project2.add_developer(user) + end + + it 'returns timelogs' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(graphql_errors).to be_nil + expect(timelog_array.size).to eq 1 + end + end end context 'when the request is correct' do @@ -74,18 +103,6 @@ RSpec.describe 'Timelogs through GroupQuery' do expect(timelogs_data).to be_empty end end - - context 'when user has no permission to read group timelogs' do - it 'returns empty result' do - guest = create(:user) - group.add_guest(guest) - post_graphql(query, current_user: guest) - - expect(response).to have_gitlab_http_status(:success) - expect(graphql_errors).to be_nil - expect(timelogs_data).to be_empty - end - end end end @@ -95,7 +112,7 @@ RSpec.describe 'Timelogs through GroupQuery' do end end - def query(timelog_params = params) + def query(timelog_params: params, full_path: group.full_path) timelog_nodes = <<~NODE nodes { spentAt @@ -114,7 +131,7 @@ RSpec.describe 'Timelogs through GroupQuery' do graphql_query_for( :group, - { full_path: group.full_path }, + { full_path: full_path }, query_graphql_field(:timelogs, timelog_params, timelog_nodes) ) end diff --git a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb index 0dcae28ac5d..0d7571d91ca 100644 --- a/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/ci_cd_settings_update_spec.rb @@ -5,8 +5,16 @@ require 'spec_helper' RSpec.describe 'CiCdSettingsUpdate' do include GraphqlHelpers - let_it_be(:project) { create(:project, keep_latest_artifact: true) } - let(:variables) { { full_path: project.full_path, keep_latest_artifact: false } } + let_it_be(:project) { create(:project, keep_latest_artifact: true, ci_job_token_scope_enabled: true) } + + let(:variables) do + { + full_path: project.full_path, + keep_latest_artifact: false, + job_token_scope_enabled: false + } + end + let(:mutation) { graphql_mutation(:ci_cd_settings_update, variables) } context 'when unauthorized' do @@ -45,6 +53,26 @@ RSpec.describe 'CiCdSettingsUpdate' do expect(project.keep_latest_artifact).to eq(false) end + it 'updates job_token_scope_enabled' do + post_graphql_mutation(mutation, current_user: user) + + project.reload + + expect(response).to have_gitlab_http_status(:success) + expect(project.ci_job_token_scope_enabled).to eq(false) + end + + it 'does not update job_token_scope_enabled if not specified' do + variables.except!(:job_token_scope_enabled) + + post_graphql_mutation(mutation, current_user: user) + + project.reload + + expect(response).to have_gitlab_http_status(:success) + expect(project.ci_job_token_scope_enabled).to eq(true) + end + context 'when bad arguments are provided' do let(:variables) { { full_path: '', keep_latest_artifact: false } } 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 new file mode 100644 index 00000000000..07b05ead651 --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_spec.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'RunnersRegistrationTokenReset' do + include GraphqlHelpers + + let(:mutation) { graphql_mutation(:runners_registration_token_reset, input) } + let(:mutation_response) { graphql_mutation_response(:runners_registration_token_reset) } + + subject { post_graphql_mutation(mutation, current_user: user) } + + shared_examples 'unauthorized' do + it 'returns an error' do + subject + + expect(graphql_errors).not_to be_empty + expect(graphql_errors).to include(a_hash_including('message' => "The resource that you are attempting to access does not exist or you don't have permission to perform this action")) + expect(mutation_response).to be_nil + end + end + + shared_context 'when unauthorized' do |scope| + context 'when unauthorized' do + let_it_be(:user) { create(:user) } + + context "when not a #{scope} member" do + it_behaves_like 'unauthorized' + end + + context "with a non-admin #{scope} member" do + before do + target.add_developer(user) + end + + it_behaves_like 'unauthorized' + end + end + end + + shared_context 'when authorized' do |scope| + it 'resets runner registration token' do + expect { subject }.to change { get_token } + expect(response).to have_gitlab_http_status(:success) + + expect(mutation_response).not_to be_nil + expect(mutation_response['errors']).to be_empty + expect(mutation_response['token']).not_to be_empty + expect(mutation_response['token']).to eq(get_token) + end + + context 'when malformed id is provided' do + let(:input) { { type: "#{scope.upcase}_TYPE", id: 'some string' } } + + it 'returns errors' do + expect { subject }.not_to change { get_token } + + expect(graphql_errors).not_to be_empty + expect(mutation_response).to be_nil + end + end + end + + context 'applied to project' do + let_it_be(:project) { create_default(: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 authorized', 'project' do + let_it_be(:user) { project.owner } + + def get_token + project.reload.runners_token + end + end + end + + context 'applied to group' do + let_it_be(:group) { create_default(: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 authorized', 'group' do + let_it_be(:user) { create_default(:group_member, :maintainer, user: create(:user), group: group ).user } + + def get_token + group.reload.runners_token + end + end + end + + context 'applied to instance' do + before do + ApplicationSetting.create_from_defaults + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + end + + let(:input) { { type: 'INSTANCE_TYPE' } } + + context 'when unauthorized' do + let(:user) { create(:user) } + + it_behaves_like 'unauthorized' + end + + include_context 'when authorized', 'instance' do + let_it_be(:user) { create(:user, :admin) } + + def get_token + ApplicationSetting.current_without_cache.runners_registration_token + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/labels/create_spec.rb b/spec/requests/api/graphql/mutations/labels/create_spec.rb index ca3ccc8e06c..28284408306 100644 --- a/spec/requests/api/graphql/mutations/labels/create_spec.rb +++ b/spec/requests/api/graphql/mutations/labels/create_spec.rb @@ -11,8 +11,7 @@ RSpec.describe Mutations::Labels::Create do { 'title' => 'foo', 'description' => 'some description', - 'color' => '#FF0000', - 'removeOnClose' => true + 'color' => '#FF0000' } end diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index d944c9e9e57..214c804c519 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -86,7 +86,7 @@ RSpec.describe 'Creating a Snippet' do it 'passes disable_spam_action_service param to service' do expect(::Snippets::CreateService) .to receive(:new) - .with(anything, anything, hash_including(disable_spam_action_service: true)) + .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true)) .and_call_original subject @@ -190,7 +190,7 @@ RSpec.describe 'Creating a Snippet' do it do expect(::Snippets::CreateService).to receive(:new) - .with(nil, user, hash_including(files: expected_value)) + .with(project: nil, current_user: user, params: hash_including(files: expected_value)) .and_return(double(execute: creation_response)) subject diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index 28ab593526a..77efb786dcb 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -90,7 +90,7 @@ RSpec.describe 'Updating a Snippet' do it 'passes disable_spam_action_service param to service' do expect(::Snippets::UpdateService) .to receive(:new) - .with(anything, anything, hash_including(disable_spam_action_service: true)) + .with(project: anything, current_user: anything, params: hash_including(disable_spam_action_service: true)) .and_call_original subject 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 705ef28ffd4..8f92105dc9c 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 @@ -22,8 +22,8 @@ RSpec.describe 'Marking all todos done' do graphql_mutation(:todos_mark_all_done, input, <<-QL.strip_heredoc clientMutationId + todos { id } errors - updatedIds QL ) end @@ -40,7 +40,7 @@ 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['updatedIds'] + 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)) end @@ -52,7 +52,7 @@ RSpec.describe 'Marking all todos done' do expect(todo3.reload.state).to eq('pending') expect(other_user_todo.reload.state).to eq('pending') - updated_todo_ids = mutation_response['updatedIds'] + updated_todo_ids = mutation_response['todos'] expect(updated_todo_ids).to be_empty end 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 3e96d5c5058..e71a232ff7c 100644 --- a/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/restore_many_spec.rb @@ -22,7 +22,6 @@ RSpec.describe 'Restoring many Todos' do <<-QL.strip_heredoc clientMutationId errors - updatedIds todos { id state @@ -44,7 +43,6 @@ RSpec.describe 'Restoring many Todos' do expect(mutation_response).to include( 'errors' => be_empty, - 'updatedIds' => match_array(input_ids), 'todos' => contain_exactly( { 'id' => global_id_of(todo1), 'state' => 'pending' }, { 'id' => global_id_of(todo2), 'state' => 'pending' } diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb index 34137a07c34..9830623ede8 100644 --- a/spec/requests/api/graphql/packages/composer_spec.rb +++ b/spec/requests/api/graphql/packages/composer_spec.rb @@ -3,62 +3,35 @@ require 'spec_helper' RSpec.describe 'package details' do include GraphqlHelpers + include_context 'package details setup' - let_it_be(:project) { create(:project) } - let_it_be(:composer_package) { create(:composer_package, project: project) } + let_it_be(:package) { create(:composer_package, project: project) } let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } } let_it_be(:composer_metadatum) do # we are forced to manually create the metadatum, without using the factory to force the sha to be a string # and avoid an error where gitaly can't find the repository - create(:composer_metadatum, package: composer_package, target_sha: 'foo_sha', composer_json: composer_json) + create(:composer_metadatum, package: package, target_sha: 'foo_sha', composer_json: composer_json) end - let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } let(:metadata) { query_graphql_fragment('ComposerMetadata') } - let(:package_files) { all_graphql_fields_for('PackageFile') } - let(:user) { project.owner } - let(:package_global_id) { global_id_of(composer_package) } - let(:package_details) { graphql_data_at(:package) } - let(:metadata_response) { graphql_data_at(:package, :metadata) } let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } - let(:query) do - graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) - #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)} - metadata { - #{metadata} - } - packageFiles { - nodes { - #{package_files} - } - } - FIELDS - end - subject { post_graphql(query, current_user: user) } before do subject end - it_behaves_like 'a working graphql query' do - it 'matches the JSON schema' do - expect(package_details).to match_schema('graphql/packages/package_details') - end - end + it_behaves_like 'a package detail' - describe 'Composer' do - it 'has the correct metadata' do - expect(metadata_response).to include( - 'targetSha' => 'foo_sha', - 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s) - ) - end + it 'has the correct metadata' do + expect(metadata_response).to include( + 'targetSha' => 'foo_sha', + 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s) + ) + end - it 'does not have files' do - expect(package_files_response).to be_empty - end + it 'does not have files' do + expect(package_files_response).to be_empty end end diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb index dc64c5057d5..84c5af33e5d 100644 --- a/spec/requests/api/graphql/packages/conan_spec.rb +++ b/spec/requests/api/graphql/packages/conan_spec.rb @@ -3,26 +3,13 @@ require 'spec_helper' RSpec.describe 'conan package details' do include GraphqlHelpers + include_context 'package details setup' - let_it_be(:project) { create(:project) } - let_it_be(:conan_package) { create(:conan_package, project: project) } + let_it_be(:package) { create(:conan_package, project: project) } - let(:package_global_id) { global_id_of(conan_package) } let(:metadata) { query_graphql_fragment('ConanMetadata') } - let(:first_file) { conan_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } } - - let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } - let(:package_files) { all_graphql_fields_for('PackageFile') } let(:package_files_metadata) {query_graphql_fragment('ConanFileMetadata')} - let(:user) { project.owner } - let(:package_details) { graphql_data_at(:package) } - let(:metadata_response) { graphql_data_at(:package, :metadata) } - let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } - let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)} - let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)} - let(:query) do graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)} @@ -46,35 +33,16 @@ RSpec.describe 'conan package details' do subject end - it_behaves_like 'a working graphql query' do - it 'matches the JSON schema' do - expect(package_details).to match_schema('graphql/packages/package_details') - end - end + it_behaves_like 'a package detail' + it_behaves_like 'a package with files' it 'has the correct metadata' do expect(metadata_response).to include( - 'id' => global_id_of(conan_package.conan_metadatum), - 'recipe' => conan_package.conan_metadatum.recipe, - 'packageChannel' => conan_package.conan_metadatum.package_channel, - 'packageUsername' => conan_package.conan_metadatum.package_username, - 'recipePath' => conan_package.conan_metadatum.recipe_path - ) - end - - it 'has the right amount of files' do - expect(package_files_response.length).to be(conan_package.package_files.length) - end - - it 'has the basic package files data' do - expect(first_file_response).to include( - 'id' => global_id_of(first_file), - 'fileName' => first_file.file_name, - 'size' => first_file.size.to_s, - 'downloadPath' => first_file.download_path, - 'fileSha1' => first_file.file_sha1, - 'fileMd5' => first_file.file_md5, - 'fileSha256' => first_file.file_sha256 + '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 ) end diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb index 8b6b5ea0986..d28d32b0df5 100644 --- a/spec/requests/api/graphql/packages/maven_spec.rb +++ b/spec/requests/api/graphql/packages/maven_spec.rb @@ -3,89 +3,51 @@ require 'spec_helper' RSpec.describe 'maven package details' do include GraphqlHelpers + include_context 'package details setup' - let_it_be(:project) { create(:project) } - let_it_be(:maven_package) { create(:maven_package, project: project) } + let_it_be(:package) { create(:maven_package, project: project) } - let(:package_global_id) { global_id_of(maven_package) } let(:metadata) { query_graphql_fragment('MavenMetadata') } - let(:first_file) { maven_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } } - - let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } - let(:package_files) { all_graphql_fields_for('PackageFile') } - - let(:user) { project.owner } - let(:package_details) { graphql_data_at(:package) } - let(:metadata_response) { graphql_data_at(:package, :metadata) } - let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } - let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)} - - let(:query) do - graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) - #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)} - metadata { - #{metadata} - } - packageFiles { - nodes { - #{package_files} - } - } - FIELDS - end - - subject { post_graphql(query, current_user: user) } - - shared_examples 'a working maven package' do - before do - subject - end - - it_behaves_like 'a working graphql query' do - it 'matches the JSON schema' do - expect(package_details).to match_schema('graphql/packages/package_details') - end - end + shared_examples 'correct maven metadata' do it 'has the correct metadata' do expect(metadata_response).to include( - 'id' => global_id_of(maven_package.maven_metadatum), - 'path' => maven_package.maven_metadatum.path, - 'appGroup' => maven_package.maven_metadatum.app_group, - 'appVersion' => maven_package.maven_metadatum.app_version, - 'appName' => maven_package.maven_metadatum.app_name + '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 ) end + end - it 'has the right amount of files' do - expect(package_files_response.length).to be(maven_package.package_files.length) - end + context 'a maven package with version' do + subject { post_graphql(query, current_user: user) } - it 'has the basic package files data' do - expect(first_file_response).to include( - 'id' => global_id_of(first_file), - 'fileName' => first_file.file_name, - 'size' => first_file.size.to_s, - 'downloadPath' => first_file.download_path, - 'fileSha1' => first_file.file_sha1, - 'fileMd5' => first_file.file_md5, - 'fileSha256' => first_file.file_sha256 - ) + before do + subject end - end - context 'a maven package with version' do - it_behaves_like "a working maven package" + it_behaves_like 'a package detail' + it_behaves_like 'correct maven metadata' + it_behaves_like 'a package with files' end context 'a versionless maven package' do let_it_be(:maven_metadatum) { create(:maven_metadatum, app_version: nil) } - let_it_be(:maven_package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) } + let_it_be(:package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) } + + subject { post_graphql(query, current_user: user) } + + before do + subject + end - it_behaves_like "a working maven package" + it_behaves_like 'a package detail' + it_behaves_like 'correct maven metadata' + it_behaves_like 'a package with files' - it "has an empty version" do + it 'has an empty version' do subject expect(metadata_response['appVersion']).to eq(nil) diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb index fa9d8a0e37e..1de16009684 100644 --- a/spec/requests/api/graphql/packages/nuget_spec.rb +++ b/spec/requests/api/graphql/packages/nuget_spec.rb @@ -3,37 +3,11 @@ require 'spec_helper' RSpec.describe 'nuget package details' do include GraphqlHelpers + include_context 'package details setup' - let_it_be(:project) { create(:project) } - let_it_be(:nuget_package) { create(:nuget_package, :with_metadatum, project: project) } + let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) } - let(:package_global_id) { global_id_of(nuget_package) } let(:metadata) { query_graphql_fragment('NugetMetadata') } - let(:first_file) { nuget_package.package_files.find { |f| global_id_of(f) == first_file_response['id'] } } - - let(:depth) { 3 } - let(:excluded) { %w[metadata apiFuzzingCiConfiguration pipeline packageFiles] } - let(:package_files) { all_graphql_fields_for('PackageFile') } - - let(:user) { project.owner } - let(:package_details) { graphql_data_at(:package) } - let(:metadata_response) { graphql_data_at(:package, :metadata) } - let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) } - let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)} - - let(:query) do - graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) - #{all_graphql_fields_for('PackageDetailsType', max_depth: depth, excluded: excluded)} - metadata { - #{metadata} - } - packageFiles { - nodes { - #{package_files} - } - } - FIELDS - end subject { post_graphql(query, current_user: user) } @@ -41,34 +15,15 @@ RSpec.describe 'nuget package details' do subject end - it_behaves_like 'a working graphql query' do - it 'matches the JSON schema' do - expect(package_details).to match_schema('graphql/packages/package_details') - end - end + it_behaves_like 'a package detail' + it_behaves_like 'a package with files' it 'has the correct metadata' do expect(metadata_response).to include( - 'id' => global_id_of(nuget_package.nuget_metadatum), - 'licenseUrl' => nuget_package.nuget_metadatum.license_url, - 'projectUrl' => nuget_package.nuget_metadatum.project_url, - 'iconUrl' => nuget_package.nuget_metadatum.icon_url - ) - end - - it 'has the right amount of files' do - expect(package_files_response.length).to be(nuget_package.package_files.length) - end - - it 'has the basic package files data' do - expect(first_file_response).to include( - 'id' => global_id_of(first_file), - 'fileName' => first_file.file_name, - 'size' => first_file.size.to_s, - 'downloadPath' => first_file.download_path, - 'fileSha1' => first_file.file_sha1, - 'fileMd5' => first_file.file_md5, - 'fileSha256' => first_file.file_sha256 + '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 ) end end diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb new file mode 100644 index 00000000000..64fe7d29a7a --- /dev/null +++ b/spec/requests/api/graphql/packages/pypi_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe 'pypi package details' do + include GraphqlHelpers + include_context 'package details setup' + + let_it_be(:package) { create(:pypi_package, project: project) } + + let(:metadata) { query_graphql_fragment('PypiMetadata') } + + subject { post_graphql(query, current_user: user) } + + before do + subject + end + + it_behaves_like 'a package detail' + 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 + ) + end +end diff --git a/spec/requests/api/graphql/project/base_service_spec.rb b/spec/requests/api/graphql/project/base_service_spec.rb index 4dfc242da80..af462c4a639 100644 --- a/spec/requests/api/graphql/project/base_service_spec.rb +++ b/spec/requests/api/graphql/project/base_service_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'query Jira service' do let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:jira_service) { create(:jira_service, project: project) } - let_it_be(:bugzilla_service) { create(:bugzilla_service, project: project) } + let_it_be(:bugzilla_integration) { create(:bugzilla_integration, project: project) } let_it_be(:redmine_service) { create(:redmine_service, project: project) } let(:query) do diff --git a/spec/requests/api/graphql/project/project_members_spec.rb b/spec/requests/api/graphql/project/project_members_spec.rb index c08bb8dc0a0..466464f600c 100644 --- a/spec/requests/api/graphql/project/project_members_spec.rb +++ b/spec/requests/api/graphql/project/project_members_spec.rb @@ -50,6 +50,20 @@ RSpec.describe 'getting project members information' do invited_group.add_guest(invited_user) end + context 'when a member is invited only via email and current_user is a maintainer' do + before do + parent_project.add_maintainer(user) + create(:project_member, :invited, source: parent_project) + end + + it 'returns null in the user field' 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) + end + end + it 'returns direct members' do fetch_members(project: child_project, args: { relations: [:DIRECT] }) diff --git a/spec/requests/api/graphql/project/releases_spec.rb b/spec/requests/api/graphql/project/releases_spec.rb index 43732c2ed18..8ccdb955ed9 100644 --- a/spec/requests/api/graphql/project/releases_spec.rb +++ b/spec/requests/api/graphql/project/releases_spec.rb @@ -295,75 +295,69 @@ RSpec.describe 'Query.project(fullPath).releases()' do end end - describe 'sorting behavior' do - let_it_be(:today) { Time.now } - let_it_be(:yesterday) { today - 1.day } - let_it_be(:tomorrow) { today + 1.day } + describe 'sorting and pagination' do + let_it_be(:sort_project) { create(:project, :public) } - let_it_be(:project) { create(:project, :repository, :public) } + let(:data_path) { [:project, :releases] } + let(:current_user) { developer } - let_it_be(:release_v1) { create(:release, project: project, tag: 'v1', released_at: yesterday, created_at: tomorrow) } - let_it_be(:release_v2) { create(:release, project: project, tag: 'v2', released_at: today, created_at: yesterday) } - let_it_be(:release_v3) { create(:release, project: project, tag: 'v3', released_at: tomorrow, created_at: today) } - - let(:current_user) { developer } - - let(:params) { nil } - - let(:sorted_tags) do - graphql_data.dig('project', 'releases', 'nodes').map { |release| release['tagName'] } - end - - let(:query) do - graphql_query_for(:project, { fullPath: project.full_path }, - %{ - releases#{params ? "(#{params})" : ""} { - nodes { - tagName - } - } - }) - end - - before do - post_query + def pagination_query(params) + graphql_query_for( + :project, + { full_path: sort_project.full_path }, + query_graphql_field(:releases, params, "#{page_info} nodes { tagName }") + ) end - context 'when no sort: parameter is provided' do - it 'returns the results with the default sort applied (sort: RELEASED_AT_DESC)' do - expect(sorted_tags).to eq(%w(v3 v2 v1)) - end + def pagination_results_data(nodes) + nodes.map { |release| release['tagName'] } end - context 'with sort: RELEASED_AT_DESC' do - let(:params) { 'sort: RELEASED_AT_DESC' } - - it 'returns the releases ordered by released_at in descending order' do - expect(sorted_tags).to eq(%w(v3 v2 v1)) + context 'when sorting by released_at' do + let_it_be(:release5) { create(:release, project: sort_project, tag: 'v5.5.0', released_at: 3.days.from_now) } + let_it_be(:release1) { create(:release, project: sort_project, tag: 'v5.1.0', released_at: 3.days.ago) } + let_it_be(:release4) { create(:release, project: sort_project, tag: 'v5.4.0', released_at: 2.days.from_now) } + let_it_be(:release2) { create(:release, project: sort_project, tag: 'v5.2.0', released_at: 2.days.ago) } + let_it_be(:release3) { create(:release, project: sort_project, tag: 'v5.3.0', released_at: 1.day.ago) } + + context 'when ascending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :RELEASED_AT_ASC } + let(:first_param) { 2 } + let(:expected_results) { [release1.tag, release2.tag, release3.tag, release4.tag, release5.tag] } + end end - end - context 'with sort: RELEASED_AT_ASC' do - let(:params) { 'sort: RELEASED_AT_ASC' } - - it 'returns the releases ordered by released_at in ascending order' do - expect(sorted_tags).to eq(%w(v1 v2 v3)) + context 'when descending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :RELEASED_AT_DESC } + let(:first_param) { 2 } + let(:expected_results) { [release5.tag, release4.tag, release3.tag, release2.tag, release1.tag] } + end end end - context 'with sort: CREATED_DESC' do - let(:params) { 'sort: CREATED_DESC' } - - it 'returns the releases ordered by created_at in descending order' do - expect(sorted_tags).to eq(%w(v1 v3 v2)) + context 'when sorting by created_at' do + let_it_be(:release5) { create(:release, project: sort_project, tag: 'v5.5.0', created_at: 3.days.from_now) } + let_it_be(:release1) { create(:release, project: sort_project, tag: 'v5.1.0', created_at: 3.days.ago) } + let_it_be(:release4) { create(:release, project: sort_project, tag: 'v5.4.0', created_at: 2.days.from_now) } + let_it_be(:release2) { create(:release, project: sort_project, tag: 'v5.2.0', created_at: 2.days.ago) } + let_it_be(:release3) { create(:release, project: sort_project, tag: 'v5.3.0', created_at: 1.day.ago) } + + context 'when ascending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :CREATED_ASC } + let(:first_param) { 2 } + let(:expected_results) { [release1.tag, release2.tag, release3.tag, release4.tag, release5.tag] } + end end - end - - context 'with sort: CREATED_ASC' do - let(:params) { 'sort: CREATED_ASC' } - it 'returns the releases ordered by created_at in ascending order' do - expect(sorted_tags).to eq(%w(v2 v3 v1)) + context 'when descending' do + it_behaves_like 'sorted paginated query' do + let(:sort_param) { :CREATED_DESC } + let(:first_param) { 2 } + let(:expected_results) { [release5.tag, release4.tag, release3.tag, release2.tag, release1.tag] } + end end end end diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index b367bbaaf43..54375d4de1d 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -65,7 +65,7 @@ RSpec.describe 'getting project information' do end it 'includes topics array' do - project.update!(tag_list: 'topic1, topic2, topic3') + project.update!(topic_list: 'topic1, topic2, topic3') post_graphql(query, current_user: current_user) @@ -119,6 +119,29 @@ RSpec.describe 'getting project information' do end end + context 'when the user has reporter access to the project' do + let(:statistics_query) do + <<~GRAPHQL + { + project(fullPath: "#{project.full_path}") { + statistics { wikiSize } + } + } + GRAPHQL + end + + before do + project.add_reporter(current_user) + create(:project_statistics, project: project, wiki_size: 100) + end + + it 'allows fetching project statistics' do + post_graphql(statistics_query, current_user: current_user) + + expect(graphql_data.dig('project', 'statistics')).to include('wikiSize' => 100.0) + end + end + context 'when the user does not have access to the project' do it 'returns an empty field' do post_graphql(query, current_user: current_user) |