diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-14 18:09:43 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-14 18:09:43 +0300 |
commit | 7a33080fff9a735cbe77968d67b13ffa92c0ffae (patch) | |
tree | 71a880649c8d3e551ec6bd94e93d08a9a9b5bde7 /spec/requests | |
parent | 9223573b85bcfdd21953f52e0d2c5cb587e366a1 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/requests')
-rw-r--r-- | spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb | 106 | ||||
-rw-r--r-- | spec/requests/api/project_packages_spec.rb | 243 |
2 files changed, 343 insertions, 6 deletions
diff --git a/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb b/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb new file mode 100644 index 00000000000..1db6f83ce4f --- /dev/null +++ b/spec/requests/api/graphql/users/set_namespace_commit_email_spec.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Setting namespace commit email', feature_category: :user_profile do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:group) { create(:group, :public) } + let(:email) { create(:email, :confirmed, user: current_user) } + let(:input) { {} } + let(:namespace_id) { group.to_global_id } + let(:email_id) { email.to_global_id } + + let(:resource_or_permission_error) do + "The resource that you are attempting to access does not exist or you don't have permission to perform this action" + end + + let(:mutation) do + variables = { + namespace_id: namespace_id, + email_id: email_id + } + graphql_mutation(:user_set_namespace_commit_email, variables.merge(input), + <<-QL.strip_heredoc + namespaceCommitEmail { + email { + id + } + } + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:user_set_namespace_commit_email) + end + + shared_examples 'success' do + it 'creates a namespace commit email' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response.dig('namespaceCommitEmail', 'email', 'id')).to eq(email.to_global_id.to_s) + expect(graphql_errors).to be_nil + end + end + + before do + group.add_reporter(current_user) + end + + context 'when current_user is nil' do + it 'returns the top level error' do + post_graphql_mutation(mutation, current_user: nil) + + expect(graphql_errors.first).to match a_hash_including( + 'message' => resource_or_permission_error) + end + end + + context 'when the user cannot access the namespace' do + let(:namespace_id) { create(:group).to_global_id } + + it 'returns the top level error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(graphql_errors).not_to be_empty + expect(graphql_errors.first).to match a_hash_including( + 'message' => resource_or_permission_error) + end + end + + context 'when the service returns an error' do + let(:email_id) { create(:email).to_global_id } + + it 'returns the error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['errors']).to contain_exactly("Email must be provided.") + expect(mutation_response['namespaceCommitEmail']).to be_nil + end + end + + context 'when namespace is a group' do + it_behaves_like 'success' + end + + context 'when namespace is a user' do + let(:namespace_id) { current_user.namespace.to_global_id } + + it_behaves_like 'success' + end + + context 'when namespace is a project' do + let_it_be(:project) { create(:project) } + + let(:namespace_id) { project.project_namespace.to_global_id } + + before do + project.add_reporter(current_user) + end + + it_behaves_like 'success' + end +end diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb index c003ae9cd48..b84b7e9c52d 100644 --- a/spec/requests/api/project_packages_spec.rb +++ b/spec/requests/api/project_packages_spec.rb @@ -3,9 +3,11 @@ require 'spec_helper' RSpec.describe API::ProjectPackages, feature_category: :package_registry do - let_it_be(:project) { create(:project, :public) } + using RSpec::Parameterized::TableSyntax - let(:user) { create(:user) } + let_it_be_with_reload(:project) { create(:project, :public) } + + let_it_be(:user) { create(:user) } let!(:package1) { create(:npm_package, :last_downloaded_at, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") } let(:package_url) { "/projects/#{project.id}/packages/#{package1.id}" } let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') } @@ -101,7 +103,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do end context 'project is private' do - let(:project) { create(:project, :private) } + let_it_be(:project) { create(:project, :private) } context 'for unauthenticated user' do it_behaves_like 'rejects packages access', :project, :no_type, :not_found @@ -235,7 +237,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do expect do get api(package_url, user) - end.not_to exceed_query_limit(control) + end.not_to exceed_query_limit(control).with_threshold(4) end end @@ -286,7 +288,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do end context 'project is private' do - let(:project) { create(:project, :private) } + let_it_be(:project) { create(:project, :private) } it 'returns 404 for non authenticated user' do get api(package_url) @@ -362,6 +364,235 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do end end + describe 'GET /projects/:id/packages/:package_id/pipelines' do + let(:package_pipelines_url) { "/projects/#{project.id}/packages/#{package1.id}/pipelines" } + + let(:tokens) do + { + personal_access_token: personal_access_token.token, + job_token: job.token + } + end + + let_it_be(:personal_access_token) { create(:personal_access_token) } + let_it_be(:user) { personal_access_token.user } + let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } + let(:headers) { {} } + + subject { get api(package_pipelines_url) } + + shared_examples 'returns package pipelines' do |expected_status| + it 'returns the first page of package pipelines' do + subject + + expect(response).to have_gitlab_http_status(expected_status) + expect(response).to match_response_schema('public_api/v4/packages/pipelines') + expect(json_response.length).to eq(3) + expect(json_response.pluck('id')).to eq(pipelines.reverse.map(&:id)) + end + end + + context 'without the need for a license' do + context 'when the package does not exist' do + let(:package_pipelines_url) { "/projects/#{project.id}/packages/0/pipelines" } + + it_behaves_like 'returning response status', :not_found + end + + context 'when there are no pipelines for the package' do + let(:package_pipelines_url) { "/projects/#{project.id}/packages/#{package2.id}/pipelines" } + + it 'returns an empty response' do + subject + + expect(response).to have_gitlab_http_status(:success) + expect(response).to match_response_schema('public_api/v4/packages/pipelines') + expect(json_response.length).to eq(0) + end + end + + context 'with valid package and pipelines' do + let!(:pipelines) do + create_list(:ci_pipeline, 3, user: user, project: project).each do |pipeline| + create(:package_build_info, package: package1, pipeline: pipeline) + end + end + + where(:visibility, :user_role, :member, :token_type, :valid_token, :shared_examples_name, :expected_status) do + :public | :developer | true | :personal_access_token | true | 'returns package pipelines' | :success + :public | :guest | true | :personal_access_token | true | 'returns package pipelines' | :success + :public | :developer | true | :personal_access_token | false | 'returning response status' | :unauthorized + :public | :guest | true | :personal_access_token | false | 'returning response status' | :unauthorized + :public | :developer | false | :personal_access_token | true | 'returns package pipelines' | :success + :public | :guest | false | :personal_access_token | true | 'returns package pipelines' | :success + :public | :developer | false | :personal_access_token | false | 'returning response status' | :unauthorized + :public | :guest | false | :personal_access_token | false | 'returning response status' | :unauthorized + :public | :anonymous | false | nil | true | 'returns package pipelines' | :success + :private | :developer | true | :personal_access_token | true | 'returns package pipelines' | :success + :private | :guest | true | :personal_access_token | true | 'returning response status' | :forbidden + :private | :developer | true | :personal_access_token | false | 'returning response status' | :unauthorized + :private | :guest | true | :personal_access_token | false | 'returning response status' | :unauthorized + :private | :developer | false | :personal_access_token | true | 'returning response status' | :not_found + :private | :guest | false | :personal_access_token | true | 'returning response status' | :not_found + :private | :developer | false | :personal_access_token | false | 'returning response status' | :unauthorized + :private | :guest | false | :personal_access_token | false | 'returning response status' | :unauthorized + :private | :anonymous | false | nil | true | 'returning response status' | :not_found + :public | :developer | true | :job_token | true | 'returns package pipelines' | :success + :public | :guest | true | :job_token | true | 'returns package pipelines' | :success + :public | :developer | true | :job_token | false | 'returning response status' | :unauthorized + :public | :guest | true | :job_token | false | 'returning response status' | :unauthorized + :public | :developer | false | :job_token | true | 'returns package pipelines' | :success + :public | :guest | false | :job_token | true | 'returns package pipelines' | :success + :public | :developer | false | :job_token | false | 'returning response status' | :unauthorized + :public | :guest | false | :job_token | false | 'returning response status' | :unauthorized + :private | :developer | true | :job_token | true | 'returns package pipelines' | :success + # TODO uncomment the spec below when https://gitlab.com/gitlab-org/gitlab/-/issues/370998 is resolved + # :private | :guest | true | :job_token | true | 'returning response status' | :forbidden + :private | :developer | true | :job_token | false | 'returning response status' | :unauthorized + :private | :guest | true | :job_token | false | 'returning response status' | :unauthorized + :private | :developer | false | :job_token | true | 'returning response status' | :not_found + :private | :guest | false | :job_token | true | 'returning response status' | :not_found + :private | :developer | false | :job_token | false | 'returning response status' | :unauthorized + :private | :guest | false | :job_token | false | 'returning response status' | :unauthorized + end + + with_them do + subject { get api(package_pipelines_url), headers: headers } + + let(:invalid_token) { 'invalid-token123' } + let(:token) { valid_token ? tokens[token_type] : invalid_token } + let(:headers) do + case token_type + when :personal_access_token + { Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => token } + when :job_token + { Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER => token } + when nil + {} + end + end + + before do + project.update!(visibility: visibility.to_s) + project.send("add_#{user_role}", user) if member && user_role != :anonymous + end + + it_behaves_like params[:shared_examples_name], params[:expected_status] + end + end + + context 'pagination' do + shared_context 'setup pipeline records' do + let!(:pipelines) do + create_list(:package_build_info, 21, :with_pipeline, package: package1) + end + end + + shared_examples 'returns the default number of pipelines' do + it do + subject + + expect(json_response.size).to eq(20) + end + end + + shared_examples 'returns an error about the invalid per_page value' do + it do + subject + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to match(/per_page does not have a valid value/) + end + end + + context 'without pagination params' do + include_context 'setup pipeline records' + + it_behaves_like 'returns the default number of pipelines' + end + + context 'with valid per_page value' do + let(:per_page) { 11 } + + subject { get api(package_pipelines_url, user), params: { per_page: per_page } } + + include_context 'setup pipeline records' + + it 'returns the correct number of pipelines' do + subject + + expect(json_response.size).to eq(per_page) + end + end + + context 'with invalid pagination params' do + subject { get api(package_pipelines_url, user), params: { per_page: per_page } } + + context 'with non-positive per_page' do + let(:per_page) { -2 } + + it_behaves_like 'returns an error about the invalid per_page value' + end + + context 'with a too high value for per_page' do + let(:per_page) { 21 } + + it_behaves_like 'returns an error about the invalid per_page value' + end + end + + context 'with valid pagination params' do + let_it_be(:package1) { create(:npm_package, :last_downloaded_at, project: project) } + let_it_be(:build_info1) { create(:package_build_info, :with_pipeline, package: package1) } + let_it_be(:build_info2) { create(:package_build_info, :with_pipeline, package: package1) } + let_it_be(:build_info3) { create(:package_build_info, :with_pipeline, package: package1) } + + let(:pipeline1) { build_info1.pipeline } + let(:pipeline2) { build_info2.pipeline } + let(:pipeline3) { build_info3.pipeline } + + let(:per_page) { 2 } + + context 'with no cursor supplied' do + subject { get api(package_pipelines_url, user), params: { per_page: per_page } } + + it 'returns first 2 pipelines' do + subject + + expect(json_response.pluck('id')).to contain_exactly(pipeline3.id, pipeline2.id) + end + end + + context 'with a cursor parameter' do + let(:cursor) { Base64.urlsafe_encode64(Gitlab::Json.dump(cursor_attributes)) } + + subject { get api(package_pipelines_url, user), params: { per_page: per_page, cursor: cursor } } + + before do + subject + end + + context 'with a cursor for the next page' do + let(:cursor_attributes) { { "id" => build_info2.id, "_kd" => "n" } } + + it 'returns the next page of records' do + expect(json_response.pluck('id')).to contain_exactly(pipeline1.id) + end + end + + context 'with a cursor for the previous page' do + let(:cursor_attributes) { { "id" => build_info1.id, "_kd" => "p" } } + + it 'returns the previous page of records' do + expect(json_response.pluck('id')).to contain_exactly(pipeline3.id, pipeline2.id) + end + end + end + end + end + end + end + describe 'DELETE /projects/:id/packages/:package_id' do context 'without the need for a license' do context 'project is public' do @@ -379,7 +610,7 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do end context 'project is private' do - let(:project) { create(:project, :private) } + let_it_be(:project) { create(:project, :private) } before do expect(::Packages::Maven::Metadata::SyncWorker).not_to receive(:perform_async) |