diff options
Diffstat (limited to 'spec/requests')
44 files changed, 1457 insertions, 296 deletions
diff --git a/spec/requests/api/api_spec.rb b/spec/requests/api/api_spec.rb index 9fd30213133..8bd6049e6fa 100644 --- a/spec/requests/api/api_spec.rb +++ b/spec/requests/api/api_spec.rb @@ -36,6 +36,12 @@ RSpec.describe API::API do expect(response).to have_gitlab_http_status(:ok) end + it 'does authorize user for head request' do + head api('/groups', personal_access_token: token) + + expect(response).to have_gitlab_http_status(:ok) + end + it 'does not authorize user for revoked token' do revoked = create(:personal_access_token, :revoked, user: user, scopes: [:read_api]) @@ -126,4 +132,34 @@ RSpec.describe API::API do get(api('/users')) end end + + describe 'supported content-types' do + context 'GET /user/:id.txt' do + let_it_be(:user) { create(:user) } + + subject { get api("/users/#{user.id}.txt", user) } + + it 'returns application/json' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.media_type).to eq('application/json') + expect(response.body).to include('{"id":') + end + + context 'when api_always_use_application_json is disabled' do + before do + stub_feature_flags(api_always_use_application_json: false) + end + + it 'returns text/plain' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.media_type).to eq('text/plain') + expect(response.body).to include('#<API::Entities::User:') + end + end + end + end end diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb index 63fbf6e32dd..ca09f5524ca 100644 --- a/spec/requests/api/applications_spec.rb +++ b/spec/requests/api/applications_spec.rb @@ -31,7 +31,7 @@ RSpec.describe API::Applications, :api do expect(response).to have_gitlab_http_status(:bad_request) expect(json_response).to be_a Hash - expect(json_response['message']['redirect_uri'][0]).to eq('must be an absolute URI.') + expect(json_response['message']['redirect_uri'][0]).to eq('must be a valid URI.') end it 'does not allow creating an application with a forbidden URI format' do diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb index 8a05d20fb33..9d63d675a02 100644 --- a/spec/requests/api/debian_group_packages_spec.rb +++ b/spec/requests/api/debian_group_packages_spec.rb @@ -6,32 +6,32 @@ RSpec.describe API::DebianGroupPackages do include WorkhorseHelpers include_context 'Debian repository shared context', :group do - describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release.gpg' do - let(:url) { "/groups/#{group.id}/-/packages/debian/dists/#{distribution}/Release.gpg" } + describe 'GET groups/:id/packages/debian/dists/*distribution/Release.gpg' do + let(:url) { "/groups/#{group.id}/packages/debian/dists/#{distribution}/Release.gpg" } it_behaves_like 'Debian group repository GET endpoint', :not_found, nil end - describe 'GET groups/:id/-/packages/debian/dists/*distribution/Release' do - let(:url) { "/groups/#{group.id}/-/packages/debian/dists/#{distribution}/Release" } + describe 'GET groups/:id/packages/debian/dists/*distribution/Release' do + let(:url) { "/groups/#{group.id}/packages/debian/dists/#{distribution}/Release" } it_behaves_like 'Debian group repository GET endpoint', :success, 'TODO Release' end - describe 'GET groups/:id/-/packages/debian/dists/*distribution/InRelease' do - let(:url) { "/groups/#{group.id}/-/packages/debian/dists/#{distribution}/InRelease" } + describe 'GET groups/:id/packages/debian/dists/*distribution/InRelease' do + let(:url) { "/groups/#{group.id}/packages/debian/dists/#{distribution}/InRelease" } it_behaves_like 'Debian group repository GET endpoint', :not_found, nil end - describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/groups/#{group.id}/-/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } + describe 'GET groups/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do + let(:url) { "/groups/#{group.id}/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } it_behaves_like 'Debian group repository GET endpoint', :success, 'TODO Packages' end - describe 'GET groups/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name' do - let(:url) { "/groups/#{group.id}/-/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } + describe 'GET groups/:id/packages/debian/pool/:component/:letter/:source_package/:file_name' do + let(:url) { "/groups/#{group.id}/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } it_behaves_like 'Debian group repository GET endpoint', :success, 'TODO File' end diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb index 663b69b1b76..4941f2a77f4 100644 --- a/spec/requests/api/debian_project_packages_spec.rb +++ b/spec/requests/api/debian_project_packages_spec.rb @@ -6,46 +6,46 @@ RSpec.describe API::DebianProjectPackages do include WorkhorseHelpers include_context 'Debian repository shared context', :project do - describe 'GET projects/:id/-/packages/debian/dists/*distribution/Release.gpg' do - let(:url) { "/projects/#{project.id}/-/packages/debian/dists/#{distribution}/Release.gpg" } + describe 'GET projects/:id/packages/debian/dists/*distribution/Release.gpg' do + let(:url) { "/projects/#{project.id}/packages/debian/dists/#{distribution}/Release.gpg" } it_behaves_like 'Debian project repository GET endpoint', :not_found, nil end - describe 'GET projects/:id/-/packages/debian/dists/*distribution/Release' do - let(:url) { "/projects/#{project.id}/-/packages/debian/dists/#{distribution}/Release" } + describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do + let(:url) { "/projects/#{project.id}/packages/debian/dists/#{distribution}/Release" } it_behaves_like 'Debian project repository GET endpoint', :success, 'TODO Release' end - describe 'GET projects/:id/-/packages/debian/dists/*distribution/InRelease' do - let(:url) { "/projects/#{project.id}/-/packages/debian/dists/#{distribution}/InRelease" } + describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do + let(:url) { "/projects/#{project.id}/packages/debian/dists/#{distribution}/InRelease" } it_behaves_like 'Debian project repository GET endpoint', :not_found, nil end - describe 'GET projects/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do - let(:url) { "/projects/#{project.id}/-/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } + describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do + let(:url) { "/projects/#{project.id}/packages/debian/dists/#{distribution}/#{component}/binary-#{architecture}/Packages" } it_behaves_like 'Debian project repository GET endpoint', :success, 'TODO Packages' end - describe 'GET projects/:id/-/packages/debian/pool/:component/:letter/:source_package/:file_name' do - let(:url) { "/projects/#{project.id}/-/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } + describe 'GET projects/:id/packages/debian/pool/:component/:letter/:source_package/:file_name' do + let(:url) { "/projects/#{project.id}/packages/debian/pool/#{component}/#{letter}/#{source_package}/#{package_name}_#{package_version}_#{architecture}.deb" } it_behaves_like 'Debian project repository GET endpoint', :success, 'TODO File' end - describe 'PUT projects/:id/-/packages/debian/incoming/:file_name' do + describe 'PUT projects/:id/packages/debian/:file_name' do let(:method) { :put } - let(:url) { "/projects/#{project.id}/-/packages/debian/incoming/#{file_name}" } + let(:url) { "/projects/#{project.id}/packages/debian/#{file_name}" } it_behaves_like 'Debian project repository PUT endpoint', :created, nil end - describe 'PUT projects/:id/-/packages/debian/incoming/:file_name/authorize' do + describe 'PUT projects/:id/packages/debian/:file_name/authorize' do let(:method) { :put } - let(:url) { "/projects/#{project.id}/-/packages/debian/incoming/#{file_name}/authorize" } + let(:url) { "/projects/#{project.id}/packages/debian/#{file_name}/authorize" } it_behaves_like 'Debian project repository PUT endpoint', :created, nil, is_authorize: true end diff --git a/spec/requests/api/deploy_tokens_spec.rb b/spec/requests/api/deploy_tokens_spec.rb index 8ec4f888e2e..7a31ff725c8 100644 --- a/spec/requests/api/deploy_tokens_spec.rb +++ b/spec/requests/api/deploy_tokens_spec.rb @@ -10,24 +10,12 @@ RSpec.describe API::DeployTokens do let!(:deploy_token) { create(:deploy_token, projects: [project]) } let!(:group_deploy_token) { create(:deploy_token, :group, groups: [group]) } - shared_examples 'with feature flag disabled' do - context 'disabled feature flag' do - before do - stub_feature_flags(deploy_tokens_api: false) - end - - it { is_expected.to have_gitlab_http_status(:service_unavailable) } - end - end - describe 'GET /deploy_tokens' do subject do get api('/deploy_tokens', user) response end - it_behaves_like 'with feature flag disabled' - context 'when unauthenticated' do let(:user) { nil } @@ -81,8 +69,6 @@ RSpec.describe API::DeployTokens do project.add_maintainer(user) end - it_behaves_like 'with feature flag disabled' - it { is_expected.to have_gitlab_http_status(:ok) } it 'returns all deploy tokens for the project' do @@ -128,8 +114,6 @@ RSpec.describe API::DeployTokens do group.add_maintainer(user) end - it_behaves_like 'with feature flag disabled' - it { is_expected.to have_gitlab_http_status(:ok) } it 'returns all deploy tokens for the group' do diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index 6a8d5f91abd..110d6e2f99e 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -55,6 +55,12 @@ RSpec.describe API::Events do expect(json_response).to be_an Array expect(json_response.size).to eq(1) end + + it 'returns "200" response on head request' do + head api('/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31', personal_access_token: token) + + expect(response).to have_gitlab_http_status(:ok) + end end context 'when the requesting token does not have "read_user" or "api" scope' do diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb index d162d288129..648d899f1a8 100644 --- a/spec/requests/api/generic_packages_spec.rb +++ b/spec/requests/api/generic_packages_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::GenericPackages do include HttpBasicAuthHelpers + using RSpec::Parameterized::TableSyntax let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:project, reload: true) { create(:project) } @@ -76,8 +77,6 @@ RSpec.describe API::GenericPackages do describe 'PUT /api/v4/projects/:id/packages/generic/:package_name/:package_version/:file_name/authorize' do context 'with valid project' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do 'PUBLIC' | :developer | true | :personal_access_token | :success 'PUBLIC' | :guest | true | :personal_access_token | :forbidden @@ -194,8 +193,6 @@ RSpec.describe API::GenericPackages do let(:params) { { file: file_upload } } context 'authentication' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do 'PUBLIC' | :guest | true | :personal_access_token | :forbidden 'PUBLIC' | :guest | true | :user_basic_auth | :forbidden @@ -373,8 +370,6 @@ RSpec.describe API::GenericPackages do end context 'application security' do - using RSpec::Parameterized::TableSyntax - where(:param_name, :param_value) do :package_name | 'my-package/../' :package_name | 'my-package%2f%2e%2e%2f' @@ -404,8 +399,6 @@ RSpec.describe API::GenericPackages do end describe 'GET /api/v4/projects/:id/packages/generic/:package_name/:package_version/:file_name' do - using RSpec::Parameterized::TableSyntax - let_it_be(:package) { create(:generic_package, project: project) } let_it_be(:package_file) { create(:package_file, :generic, package: package) } @@ -527,8 +520,6 @@ RSpec.describe API::GenericPackages do end context 'application security' do - using RSpec::Parameterized::TableSyntax - where(:param_name, :param_value) do :package_name | 'my-package/../' :package_name | 'my-package%2f%2e%2e%2f' diff --git a/spec/requests/api/graphql/issue/issue_spec.rb b/spec/requests/api/graphql/issue/issue_spec.rb index 42c8e0cc9c0..09e89f65882 100644 --- a/spec/requests/api/graphql/issue/issue_spec.rb +++ b/spec/requests/api/graphql/issue/issue_spec.rb @@ -24,6 +24,20 @@ RSpec.describe 'Query.issue(id)' do end end + it_behaves_like 'a noteable graphql type we can query' do + let(:noteable) { issue } + let(:project) { issue.project } + let(:path_to_noteable) { [:issue] } + + before do + project.add_guest(current_user) + end + + def query(fields) + graphql_query_for('issue', issue_params, fields) + end + end + context 'when the user does not have access to the issue' do it 'returns nil' do project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE) diff --git a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb index bf7eb3d980c..18cbb7d8b00 100644 --- a/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'Updating an existing HTTP Integration' do include GraphqlHelpers - let_it_be(:user) { create(:user) } + let_it_be(:current_user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:integration) { create(:alert_management_http_integration, project: project) } @@ -32,18 +32,8 @@ RSpec.describe 'Updating an existing HTTP Integration' do let(:mutation_response) { graphql_mutation_response(:http_integration_update) } before do - project.add_maintainer(user) + project.add_maintainer(current_user) end - it 'updates the integration' do - post_graphql_mutation(mutation, current_user: user) - - integration_response = mutation_response['integration'] - - expect(response).to have_gitlab_http_status(:success) - expect(integration_response['id']).to eq(GitlabSchema.id_from_object(integration).to_s) - expect(integration_response['name']).to eq('Modified Name') - expect(integration_response['active']).to be_falsey - expect(integration_response['url']).to include('modified-name') - end + it_behaves_like 'updating an existing HTTP integration' end diff --git a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb new file mode 100644 index 00000000000..2e4f35cbcde --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Setting assignees of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request, reviewers: [user]) } + let(:project) { merge_request.project } + let(:user) { create(:user) } + 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_reviewer_rereview, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_reviewer_rereview) + end + + def mutation_errors + mutation_response['errors'] + end + + before do + project.add_developer(current_user) + project.add_developer(user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + describe 'reviewer does not exist' 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 + + describe 'reviewer exists' do + it 'does not return an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).to be_empty + end + end +end diff --git a/spec/requests/api/graphql/mutations/releases/create_spec.rb b/spec/requests/api/graphql/mutations/releases/create_spec.rb index 79bdcec7944..a4918cd560c 100644 --- a/spec/requests/api/graphql/mutations/releases/create_spec.rb +++ b/spec/requests/api/graphql/mutations/releases/create_spec.rb @@ -309,10 +309,7 @@ RSpec.describe 'Creation of a new release' do let(:asset_link_2) { { name: 'My link', url: 'https://example.com/2' } } let(:assets) { { links: [asset_link_1, asset_link_2] } } - # Right now the raw Postgres error message is sent to the user as the validation message. - # We should catch this validation error and return a nicer message: - # https://gitlab.com/gitlab-org/gitlab/-/issues/277087 - it_behaves_like 'errors-as-data with message', 'PG::UniqueViolation' + it_behaves_like 'errors-as-data with message', %r{Validation failed: Links have duplicate values \(My link\)} end context 'when two release assets share the same URL' do @@ -320,8 +317,7 @@ RSpec.describe 'Creation of a new release' do let(:asset_link_2) { { name: 'My second link', url: 'https://example.com' } } let(:assets) { { links: [asset_link_1, asset_link_2] } } - # Same note as above about the ugly error message - it_behaves_like 'errors-as-data with message', 'PG::UniqueViolation' + it_behaves_like 'errors-as-data with message', %r{Validation failed: Links have duplicate values \(https://example.com\)} end context 'when the provided tag name is HEAD' do diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index fd0dc98a8d3..1c2260070ec 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -17,6 +17,7 @@ RSpec.describe 'Creating a Snippet' do let(:actions) { [{ action: action }.merge(file_1), { action: action }.merge(file_2)] } let(:project_path) { nil } let(:uploaded_files) { nil } + let(:spam_mutation_vars) { {} } let(:mutation_vars) do { description: description, @@ -25,7 +26,7 @@ RSpec.describe 'Creating a Snippet' do project_path: project_path, uploaded_files: uploaded_files, blob_actions: actions - } + }.merge(spam_mutation_vars) end let(:mutation) do @@ -77,7 +78,20 @@ RSpec.describe 'Creating a Snippet' do expect(mutation_response['snippet']).to be_nil end - it_behaves_like 'spam flag is present' + context 'when snippet_spam flag is disabled' do + before do + stub_feature_flags(snippet_spam: false) + end + + 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)) + .and_call_original + + subject + end + end end shared_examples 'creates snippet' do @@ -98,15 +112,24 @@ RSpec.describe 'Creating a Snippet' do end context 'when action is invalid' do - let(:file_1) { { filePath: 'example_file1' }} + let(:file_1) { { filePath: 'example_file1' } } it_behaves_like 'a mutation that returns errors in the response', errors: ['Snippet actions have invalid data'] it_behaves_like 'does not create snippet' end it_behaves_like 'snippet edit usage data counters' - it_behaves_like 'spam flag is present' - it_behaves_like 'can raise spam flag' do + + it_behaves_like 'a mutation which can mutate a spammable' do + let(:captcha_response) { 'abc123' } + let(:spam_log_id) { 1234 } + let(:spam_mutation_vars) do + { + captcha_response: captcha_response, + spam_log_id: spam_log_id + } + end + let(:service) { Snippets::CreateService } end end @@ -148,9 +171,6 @@ RSpec.describe 'Creating a Snippet' do it_behaves_like 'a mutation that returns errors in the response', errors: ["Title can't be blank"] it_behaves_like 'does not create snippet' - it_behaves_like 'can raise spam flag' do - let(:service) { Snippets::CreateService } - end end context 'when there non ActiveRecord errors' do diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index 21d403c6f73..43dc8d8bc44 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -16,6 +16,7 @@ RSpec.describe 'Updating a Snippet' do let(:updated_file) { 'CHANGELOG' } let(:deleted_file) { 'README' } let(:snippet_gid) { GitlabSchema.id_from_object(snippet).to_s } + let(:spam_mutation_vars) { {} } let(:mutation_vars) do { id: snippet_gid, @@ -26,7 +27,7 @@ RSpec.describe 'Updating a Snippet' do { action: :update, filePath: updated_file, content: updated_content }, { action: :delete, filePath: deleted_file } ] - } + }.merge(spam_mutation_vars) end let(:mutation) do @@ -81,11 +82,20 @@ RSpec.describe 'Updating a Snippet' do end end - it_behaves_like 'can raise spam flag' do - let(:service) { Snippets::UpdateService } - end + context 'when snippet_spam flag is disabled' do + before do + stub_feature_flags(snippet_spam: false) + end - it_behaves_like 'spam flag is present' + 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)) + .and_call_original + + subject + end + end context 'when there are ActiveRecord validation errors' do let(:updated_title) { '' } @@ -112,11 +122,19 @@ RSpec.describe 'Updating a Snippet' do expect(mutation_response['snippet']['visibilityLevel']).to eq('private') end end + end - it_behaves_like 'spam flag is present' - it_behaves_like 'can raise spam flag' do - let(:service) { Snippets::UpdateService } + it_behaves_like 'a mutation which can mutate a spammable' do + let(:captcha_response) { 'abc123' } + let(:spam_log_id) { 1234 } + let(:spam_mutation_vars) do + { + captcha_response: captcha_response, + spam_log_id: spam_log_id + } end + + let(:service) { Snippets::UpdateService } end def blob_at(filename) diff --git a/spec/requests/api/graphql/packages/package_composer_details_spec.rb b/spec/requests/api/graphql/packages/package_composer_details_spec.rb deleted file mode 100644 index 1a2cf4a972a..00000000000 --- a/spec/requests/api/graphql/packages/package_composer_details_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -RSpec.describe 'package composer details' do - using RSpec::Parameterized::TableSyntax - include GraphqlHelpers - - let_it_be(:project) { create(:project) } - let_it_be(:package) { create(:composer_package, project: project) } - 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: package, target_sha: 'foo_sha', composer_json: { name: 'name', type: 'type', license: 'license', version: 1 }) - end - - let(:query) do - graphql_query_for( - 'packageComposerDetails', - { id: package_global_id }, - all_graphql_fields_for('PackageComposerDetails', max_depth: 2) - ) - end - - let(:user) { project.owner } - let(:package_global_id) { package.to_global_id.to_s } - let(:package_composer_details_response) { graphql_data.dig('packageComposerDetails') } - - subject { post_graphql(query, current_user: user) } - - it_behaves_like 'a working graphql query' do - before do - subject - end - - it 'matches the JSON schema' do - expect(package_composer_details_response).to match_schema('graphql/packages/package_composer_details') - end - end -end diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb new file mode 100644 index 00000000000..3def9b7d19d --- /dev/null +++ b/spec/requests/api/graphql/packages/package_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe 'package details' do + using RSpec::Parameterized::TableSyntax + include GraphqlHelpers + + let_it_be(:project) { create(: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: package, target_sha: 'foo_sha', composer_json: composer_json) + end + + let(:depth) { 3 } + let(:excluded) { ['metadata'] } + + let(:query) do + graphql_query_for(:package, { id: package_global_id }, <<~FIELDS) + #{all_graphql_fields_for('Package', max_depth: depth, excluded: excluded)} + metadata { + #{query_graphql_fragment('ComposerMetadata')} + } + FIELDS + end + + let(:user) { project.owner } + let(:package_global_id) { global_id_of(package) } + let(:package_details) { graphql_data_at(:package) } + + subject { post_graphql(query, current_user: user) } + + it_behaves_like 'a working graphql query' do + before do + subject + end + + it 'matches the JSON schema' do + expect(package_details).to match_schema('graphql/packages/package_details') + end + + it 'includes the fields of the correct package' do + expect(package_details).to include( + 'id' => package_global_id, + 'metadata' => { + 'targetSha' => 'foo_sha', + 'composerJson' => composer_json.transform_keys(&:to_s).transform_values(&:to_s) + } + ) + 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: 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)).not_to be_present + end + end + end +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 a671ddc7ab1..7148750b6cb 100644 --- a/spec/requests/api/graphql/project/issue/designs/notes_spec.rb +++ b/spec/requests/api/graphql/project/issue/designs/notes_spec.rb @@ -22,6 +22,23 @@ RSpec.describe 'Getting designs related to an issue' do end end + it_behaves_like 'a noteable graphql type we can query' do + let(:noteable) { design } + let(:note_factory) { :diff_note_on_design } + let(:discussion_factory) { :diff_note_on_design } + let(:path_to_noteable) { [:issue, :design_collection, :designs, :nodes, 0] } + + before do + project.add_developer(current_user) + end + + def query(fields) + graphql_query_for(:issue, { id: global_id_of(issue) }, <<~FIELDS) + designCollection { designs { nodes { #{fields} } } } + FIELDS + end + end + it 'is not too deep for anonymous users' do note_fields = <<~FIELDS id @@ -37,7 +54,7 @@ RSpec.describe 'Getting designs related to an issue' do expect(note_data['id']).to eq(note.to_global_id.to_s) end - def query(note_fields = all_graphql_fields_for(Note)) + def query(note_fields = all_graphql_fields_for(Note, max_depth: 1)) design_node = <<~NODE designs { nodes { diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb index c85cb8b2ffe..a11e758c136 100644 --- a/spec/requests/api/graphql/project/merge_requests_spec.rb +++ b/spec/requests/api/graphql/project/merge_requests_spec.rb @@ -196,17 +196,18 @@ RSpec.describe 'getting merge request listings nested in a project' do end context 'when requesting `commit_count`' do - let(:requested_fields) { [:commit_count] } + let(:merge_request_with_commits) { create(:merge_request, source_project: project) } + let(:search_params) { { iids: [merge_request_a.iid.to_s, merge_request_with_commits.iid.to_s] } } + let(:requested_fields) { [:iid, :commit_count] } it 'exposes `commit_count`' do - merge_request_a.metrics.update!(commits_count: 5) - execute_query - expect(results).to include(a_hash_including('commitCount' => 5)) + expect(results).to match_array([ + { "iid" => merge_request_a.iid.to_s, "commitCount" => 0 }, + { "iid" => merge_request_with_commits.iid.to_s, "commitCount" => 29 } + ]) end - - include_examples 'N+1 query check' end context 'when requesting `merged_at`' do @@ -396,4 +397,85 @@ RSpec.describe 'getting merge request listings nested in a project' do end end end + + context 'when only the count is requested' do + 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)) + 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) { + count + } + QUERY + ) + end + + shared_examples 'count examples' do + it 'returns the correct count' do + post_graphql(query, current_user: current_user) + + count = graphql_data.dig('project', 'mergeRequests', 'count') + expect(count).to eq(1) + end + end + + context 'when "optimized_merge_request_count_with_merged_at_filter" feature flag is enabled' do + before do + stub_feature_flags(optimized_merge_request_count_with_merged_at_filter: true) + end + + 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] + 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(:query) do + graphql_query_for(:project, { full_path: project.full_path }, + <<~QUERY + mergeRequests(mergedAfter: "2020-01-01", mergedBefore: "2020-01-05", first: 0) { + totalTimeToMerge + count + } + QUERY + ) + end + + it 'does not query the merge requests table for the 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/)) + end + end + + it_behaves_like 'count examples' + + context 'when "optimized_merge_request_count_with_merged_at_filter" feature flag is disabled' do + before do + stub_feature_flags(optimized_merge_request_count_with_merged_at_filter: false) + end + + it 'queries 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] + expect(queries).to include(match(/SELECT COUNT\(\*\) FROM "merge_requests"/)) + expect(queries).not_to include(match(/SELECT COUNT\(\*\) FROM "merge_request_metrics"/)) + end + + it_behaves_like 'count examples' + end + end + end + end end diff --git a/spec/requests/api/graphql/project/packages_spec.rb b/spec/requests/api/graphql/project/packages_spec.rb index 5df98ed1e6b..b20c96d54c8 100644 --- a/spec/requests/api/graphql/project/packages_spec.rb +++ b/spec/requests/api/graphql/project/packages_spec.rb @@ -5,16 +5,27 @@ require 'spec_helper' RSpec.describe 'getting a package list for a project' do include GraphqlHelpers - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, :repository) } let_it_be(:current_user) { create(:user) } + let_it_be(:package) { create(:package, project: project) } - let(:packages_data) { graphql_data['project']['packages']['edges'] } + let_it_be(:maven_package) { create(:maven_package, project: project) } + let_it_be(:debian_package) { create(:debian_package, project: project) } + let_it_be(:composer_package) { create(:composer_package, project: project) } + let_it_be(:composer_metadatum) do + create(:composer_metadatum, package: composer_package, + target_sha: 'afdeh', + composer_json: { name: 'x', type: 'y', license: 'z', version: 1 }) + end + + let(:package_names) { graphql_data_at(:project, :packages, :edges, :node, :name) } let(:fields) do <<~QUERY edges { node { - #{all_graphql_fields_for('packages'.classify)} + #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} + metadata { #{query_graphql_fragment('ComposerMetadata')} } } } QUERY @@ -37,7 +48,17 @@ RSpec.describe 'getting a package list for a project' do it_behaves_like 'a working graphql query' it 'returns packages successfully' do - expect(packages_data[0]['node']['name']).to eq package.name + expect(package_names).to contain_exactly( + package.name, + maven_package.name, + debian_package.name, + composer_package.name + ) + end + + it 'deals with metadata' do + target_shas = graphql_data_at(:project, :packages, :edges, :node, :metadata, :target_sha) + expect(target_shas).to contain_exactly(composer_metadatum.target_sha) end end @@ -53,7 +74,7 @@ RSpec.describe 'getting a package list for a project' do end end - context 'when the user is not autenthicated' do + context 'when the user is not authenticated' do before do post_graphql(query) end diff --git a/spec/requests/api/graphql/project/release_spec.rb b/spec/requests/api/graphql/project/release_spec.rb index ccc2825da25..963b594a202 100644 --- a/spec/requests/api/graphql/project/release_spec.rb +++ b/spec/requests/api/graphql/project/release_spec.rb @@ -283,7 +283,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do let_it_be(:project) { create(:project, :repository, :private) } let_it_be(:milestone_1) { create(:milestone, project: project) } let_it_be(:milestone_2) { create(:milestone, project: project) } - let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } + let_it_be(:release, reload: true) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } let_it_be(:release_link_1) { create(:release_link, release: release) } let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) } @@ -324,7 +324,7 @@ RSpec.describe 'Query.project(fullPath).release(tagName)' do let_it_be(:project) { create(:project, :repository, :public) } let_it_be(:milestone_1) { create(:milestone, project: project) } let_it_be(:milestone_2) { create(:milestone, project: project) } - let_it_be(:release) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } + let_it_be(:release, reload: true) { create(:release, :with_evidence, project: project, milestones: [milestone_1, milestone_2], released_at: released_at) } let_it_be(:release_link_1) { create(:release_link, release: release) } let_it_be(:release_link_2) { create(:release_link, release: release, filepath: link_filepath) } diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb new file mode 100644 index 00000000000..9f1d9ab204a --- /dev/null +++ b/spec/requests/api/graphql/project/terraform/state_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'query a single terraform state' do + include GraphqlHelpers + include ::API::Helpers::RelatedResourcesHelpers + + let_it_be(:terraform_state) { create(:terraform_state, :with_version, :locked) } + + let(:latest_version) { terraform_state.latest_version } + let(:project) { terraform_state.project } + let(:current_user) { project.creator } + let(:data) { graphql_data.dig('project', 'terraformState') } + + let(:query) do + graphql_query_for( + :project, + { fullPath: project.full_path }, + query_graphql_field( + :terraformState, + { name: terraform_state.name }, + %{ + id + name + lockedAt + createdAt + updatedAt + + latestVersion { + id + serial + createdAt + updatedAt + + createdByUser { + id + } + + job { + name + } + } + + lockedByUser { + id + } + } + ) + ) + end + + before do + post_graphql(query, current_user: current_user) + end + + 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, + '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)), + '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)) }, + 'job' => { 'name' => eq(latest_version.build.name) } + } + })) + end + + context 'unauthorized users' do + let(:current_user) { nil } + + it { expect(data).to be_nil } + end +end diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb index 72621e2ce5e..c677e68b285 100644 --- a/spec/requests/api/group_labels_spec.rb +++ b/spec/requests/api/group_labels_spec.rb @@ -3,13 +3,19 @@ require 'spec_helper' RSpec.describe API::GroupLabels do + let_it_be(:valid_group_label_title_1) { 'Label foo & bar:subgroup::v.1' } + let_it_be(:valid_group_label_title_1_esc) { ERB::Util.url_encode(valid_group_label_title_1) } + let_it_be(:valid_group_label_title_2) { 'Bar & foo:subgroup::v.2' } + let_it_be(:valid_subgroup_label_title_1) { 'Support label foobar:sub::v.1' } + let_it_be(:valid_new_label_title) { 'New & foo:feature::v.3' } + let(:user) { create(:user) } let(:group) { create(:group) } let(:subgroup) { create(:group, parent: group) } let!(:group_member) { create(:group_member, group: group, user: user) } - let!(:group_label1) { create(:group_label, title: 'feature-label', group: group) } - let!(:group_label2) { create(:group_label, title: 'bug', group: group) } - let!(:subgroup_label) { create(:group_label, title: 'support-label', group: subgroup) } + let!(:group_label1) { create(:group_label, title: valid_group_label_title_1, group: group) } + let!(:group_label2) { create(:group_label, title: valid_group_label_title_2, group: group) } + let!(:subgroup_label) { create(:group_label, title: valid_subgroup_label_title_1, group: subgroup) } describe 'GET :id/labels' do context 'get current group labels' do @@ -104,7 +110,7 @@ RSpec.describe API::GroupLabels do describe 'GET :id/labels/:label_id' do it 'returns a single label for the group' do - get api("/groups/#{group.id}/labels/#{group_label1.name}", user) + get api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user) expect(response).to have_gitlab_http_status(:ok) expect(json_response['name']).to eq(group_label1.name) @@ -117,13 +123,13 @@ RSpec.describe API::GroupLabels do it 'returns created label when all params are given' do post api("/groups/#{group.id}/labels", user), params: { - name: 'Foo', + name: valid_new_label_title, color: '#FFAABB', description: 'test' } expect(response).to have_gitlab_http_status(:created) - expect(json_response['name']).to eq('Foo') + expect(json_response['name']).to eq(valid_new_label_title) expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to eq('test') end @@ -131,12 +137,12 @@ RSpec.describe API::GroupLabels do it 'returns created label when only required params are given' do post api("/groups/#{group.id}/labels", user), params: { - name: 'Foo & Bar', + name: valid_new_label_title, color: '#FFAABB' } expect(response).to have_gitlab_http_status(:created) - expect(json_response['name']).to eq('Foo & Bar') + expect(json_response['name']).to eq(valid_new_label_title) expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to be_nil end @@ -204,7 +210,7 @@ RSpec.describe API::GroupLabels do describe 'DELETE /groups/:id/labels/:label_id' do it 'returns 204 for existing label' do - delete api("/groups/#{group.id}/labels/#{group_label1.name}", user) + delete api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user) expect(response).to have_gitlab_http_status(:no_content) end @@ -228,7 +234,7 @@ RSpec.describe API::GroupLabels do end it_behaves_like '412 response' do - let(:request) { api("/groups/#{group.id}/labels/#{group_label1.name}", user) } + let(:request) { api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user) } end end @@ -237,13 +243,13 @@ RSpec.describe API::GroupLabels do put api("/groups/#{group.id}/labels", user), params: { name: group_label1.name, - new_name: 'New Label', + new_name: valid_new_label_title, color: '#FFFFFF', description: 'test' } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('New Label') + expect(json_response['name']).to eq(valid_new_label_title) expect(json_response['color']).to eq('#FFFFFF') expect(json_response['description']).to eq('test') end @@ -255,11 +261,11 @@ RSpec.describe API::GroupLabels do put api("/groups/#{subgroup.id}/labels", user), params: { name: subgroup_label.name, - new_name: 'New Label' + new_name: valid_new_label_title } expect(response).to have_gitlab_http_status(:ok) - expect(subgroup.labels[0].name).to eq('New Label') + expect(subgroup.labels[0].name).to eq(valid_new_label_title) expect(group_label1.name).to eq(group_label1.title) end @@ -267,7 +273,7 @@ RSpec.describe API::GroupLabels do put api("/groups/#{group.id}/labels", user), params: { name: 'not_exists', - new_name: 'label3' + new_name: valid_new_label_title } expect(response).to have_gitlab_http_status(:not_found) @@ -291,15 +297,15 @@ RSpec.describe API::GroupLabels do describe 'PUT /groups/:id/labels/:label_id' do it 'returns 200 if name and colors and description are changed' do - put api("/groups/#{group.id}/labels/#{group_label1.name}", user), + put api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user), params: { - new_name: 'New Label', + new_name: valid_new_label_title, color: '#FFFFFF', description: 'test' } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('New Label') + expect(json_response['name']).to eq(valid_new_label_title) expect(json_response['color']).to eq('#FFFFFF') expect(json_response['description']).to eq('test') end @@ -310,25 +316,25 @@ RSpec.describe API::GroupLabels do put api("/groups/#{subgroup.id}/labels/#{subgroup_label.name}", user), params: { - new_name: 'New Label' + new_name: valid_new_label_title } expect(response).to have_gitlab_http_status(:ok) - expect(subgroup.labels[0].name).to eq('New Label') + expect(subgroup.labels[0].name).to eq(valid_new_label_title) expect(group_label1.name).to eq(group_label1.title) end it 'returns 404 if label does not exist' do put api("/groups/#{group.id}/labels/not_exists", user), params: { - new_name: 'label3' + new_name: valid_new_label_title } expect(response).to have_gitlab_http_status(:not_found) end it 'returns 400 if no new parameters given' do - put api("/groups/#{group.id}/labels/#{group_label1.name}", user) + put api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}", user) expect(response).to have_gitlab_http_status(:bad_request) expect(json_response['error']).to eq('new_name, color, description are missing, '\ @@ -339,7 +345,7 @@ RSpec.describe API::GroupLabels do describe 'POST /groups/:id/labels/:label_id/subscribe' do context 'when label_id is a label title' do it 'subscribes to the label' do - post api("/groups/#{group.id}/labels/#{group_label1.title}/subscribe", user) + post api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}/subscribe", user) expect(response).to have_gitlab_http_status(:created) expect(json_response['name']).to eq(group_label1.title) @@ -385,7 +391,7 @@ RSpec.describe API::GroupLabels do context 'when label_id is a label title' do it 'unsubscribes from the label' do - post api("/groups/#{group.id}/labels/#{group_label1.title}/unsubscribe", user) + post api("/groups/#{group.id}/labels/#{valid_group_label_title_1_esc}/unsubscribe", user) expect(response).to have_gitlab_http_status(:created) expect(json_response['name']).to eq(group_label1.title) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index c7756a4fae5..1c359b6e50f 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -54,7 +54,7 @@ RSpec.describe API::Groups do it_behaves_like 'invalid file upload request' end - context 'when file format is not supported' do + context 'when file is too large' do let(:file_path) { 'spec/fixtures/big-image.png' } let(:message) { 'is too big' } @@ -661,6 +661,7 @@ RSpec.describe API::Groups do describe 'PUT /groups/:id' do let(:new_group_name) { 'New Group'} + let(:file_path) { 'spec/fixtures/dk.png' } it_behaves_like 'group avatar upload' do def make_upload_request @@ -678,7 +679,8 @@ RSpec.describe API::Groups do request_access_enabled: true, project_creation_level: "noone", subgroup_creation_level: "maintainer", - default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS + default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS, + avatar: fixture_file_upload(file_path) } expect(response).to have_gitlab_http_status(:ok) @@ -701,6 +703,7 @@ RSpec.describe API::Groups do expect(json_response['shared_projects']).to be_an Array expect(json_response['shared_projects'].length).to eq(0) expect(json_response['default_branch_protection']).to eq(::Gitlab::Access::MAINTAINER_PROJECT_ACCESS) + expect(json_response['avatar_url']).to end_with('dk.png') end context 'updating the `default_branch_protection` attribute' do diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index e04f63befd0..be4ecd0a734 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1094,6 +1094,104 @@ RSpec.describe API::Internal::Base do expect(response).to have_gitlab_http_status(:unauthorized) end end + + context 'admin mode' do + shared_examples 'pushes succeed for ssh and http' do + it 'accepts the SSH push' do + push(key, project) + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'accepts the HTTP push' do + push(key, project, 'http') + + expect(response).to have_gitlab_http_status(:ok) + end + end + + shared_examples 'pushes fail for ssh and http' do + it 'rejects the SSH push' do + push(key, project) + + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'rejects the HTTP push' do + push(key, project, 'http') + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'feature flag :user_mode_in_session is enabled' do + context 'with an admin user' do + let(:user) { create(:admin) } + + context 'is member of the project' do + before do + project.add_developer(user) + end + + it_behaves_like 'pushes succeed for ssh and http' + end + + context 'is not member of the project' do + it_behaves_like 'pushes succeed for ssh and http' + end + end + + context 'with a regular user' do + context 'is member of the project' do + before do + project.add_developer(user) + end + + it_behaves_like 'pushes succeed for ssh and http' + end + + context 'is not member of the project' do + it_behaves_like 'pushes fail for ssh and http' + end + end + end + + context 'feature flag :user_mode_in_session is disabled' do + before do + stub_feature_flags(user_mode_in_session: false) + end + + context 'with an admin user' do + let(:user) { create(:admin) } + + context 'is member of the project' do + before do + project.add_developer(user) + end + + it_behaves_like 'pushes succeed for ssh and http' + end + + context 'is not member of the project' do + it_behaves_like 'pushes succeed for ssh and http' + end + end + + context 'with a regular user' do + context 'is member of the project' do + before do + project.add_developer(user) + end + + it_behaves_like 'pushes succeed for ssh and http' + end + + context 'is not member of the project' do + it_behaves_like 'pushes fail for ssh and http' + end + end + end + end end describe 'POST /internal/post_receive', :clean_gitlab_redis_shared_state do diff --git a/spec/requests/api/internal/kubernetes_spec.rb b/spec/requests/api/internal/kubernetes_spec.rb index afff3647b91..2e13016a0a6 100644 --- a/spec/requests/api/internal/kubernetes_spec.rb +++ b/spec/requests/api/internal/kubernetes_spec.rb @@ -62,25 +62,25 @@ RSpec.describe API::Internal::Kubernetes do let!(:agent_token) { create(:cluster_agent_token) } it 'returns no_content for valid gitops_sync_count' do - send_request(params: { gitops_sync_count: 10 }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + send_request(params: { gitops_sync_count: 10 }) expect(response).to have_gitlab_http_status(:no_content) end it 'returns no_content 0 gitops_sync_count' do - send_request(params: { gitops_sync_count: 0 }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + send_request(params: { gitops_sync_count: 0 }) expect(response).to have_gitlab_http_status(:no_content) end it 'returns 400 for non number' do - send_request(params: { gitops_sync_count: 'string' }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + send_request(params: { gitops_sync_count: 'string' }) expect(response).to have_gitlab_http_status(:bad_request) end it 'returns 400 for negative number' do - send_request(params: { gitops_sync_count: '-1' }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + send_request(params: { gitops_sync_count: '-1' }) expect(response).to have_gitlab_http_status(:bad_request) end @@ -125,6 +125,36 @@ RSpec.describe API::Internal::Kubernetes do ) ) end + + context 'on GitLab.com' do + before do + allow(::Gitlab).to receive(:com?).and_return(true) + end + + context 'kubernetes_agent_on_gitlab_com feature flag disabled' do + before do + stub_feature_flags(kubernetes_agent_on_gitlab_com: false) + end + + it 'returns 403' do + send_request(headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'kubernetes_agent_on_gitlab_com feature flag enabled' do + before do + stub_feature_flags(kubernetes_agent_on_gitlab_com: agent_token.agent.project) + end + + it 'returns success' do + send_request(headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + + expect(response).to have_gitlab_http_status(:success) + end + end + end end end @@ -174,6 +204,36 @@ RSpec.describe API::Internal::Kubernetes do expect(response).to have_gitlab_http_status(:not_found) end end + + context 'on GitLab.com' do + before do + allow(::Gitlab).to receive(:com?).and_return(true) + end + + context 'kubernetes_agent_on_gitlab_com feature flag disabled' do + before do + stub_feature_flags(kubernetes_agent_on_gitlab_com: false) + end + + it 'returns 403' do + send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'kubernetes_agent_on_gitlab_com feature flag enabled' do + before do + stub_feature_flags(kubernetes_agent_on_gitlab_com: agent_token.agent.project) + end + + it 'returns success' do + send_request(params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }) + + expect(response).to have_gitlab_http_status(:success) + end + end + end end context 'project is private' do diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 6db6de4b533..e3fffd3e3fd 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -10,14 +10,19 @@ RSpec.describe API::Labels do else label_id = spec_params[:name] || spec_params[:label_id] - put api("/projects/#{project.id}/labels/#{label_id}", user), + put api("/projects/#{project.id}/labels/#{ERB::Util.url_encode(label_id)}", user), params: request_params.merge(spec_params.except(:name, :id)) end end + let_it_be(:valid_label_title_1) { 'Label foo & bar:subgroup::v.1' } + let_it_be(:valid_label_title_1_esc) { ERB::Util.url_encode(valid_label_title_1) } + let_it_be(:valid_label_title_2) { 'Label bar & foo:subgroup::v.2' } + let_it_be(:valid_group_label_title_1) { 'Group label foobar:sub::v.1' } + let(:user) { create(:user) } let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let!(:label1) { create(:label, description: 'the best label', title: 'label1', project: project) } + let!(:label1) { create(:label, description: 'the best label v.1', title: valid_label_title_1, project: project) } let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) } route_types = [:deprecated, :rest] @@ -25,10 +30,10 @@ RSpec.describe API::Labels do shared_examples 'label update API' do route_types.each do |route_type| it "returns 200 if name is changed (#{route_type} route)" do - put_labels_api(route_type, user, spec_params, new_name: 'New Label') + put_labels_api(route_type, user, spec_params, new_name: valid_label_title_2) expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('New Label') + expect(json_response['name']).to eq(valid_label_title_2) expect(json_response['color']).to eq(label1.color) end @@ -77,10 +82,10 @@ RSpec.describe API::Labels do end it "returns 200 if name and colors and description are changed (#{route_type} route)" do - put_labels_api(route_type, user, spec_params, new_name: 'New Label', color: '#FFFFFF', description: 'test') + put_labels_api(route_type, user, spec_params, new_name: valid_label_title_2, color: '#FFFFFF', description: 'test') expect(response).to have_gitlab_http_status(:ok) - expect(json_response['name']).to eq('New Label') + expect(json_response['name']).to eq(valid_label_title_2) expect(json_response['color']).to eq('#FFFFFF') expect(json_response['description']).to eq('test') end @@ -141,7 +146,7 @@ RSpec.describe API::Labels do priority: nil }.merge(spec_params.except(:name, :id)) - put api("/projects/#{project.id}/labels/#{label_id}", user), + put api("/projects/#{project.id}/labels/#{ERB::Util.url_encode(label_id)}", user), params: request_params expect(response).to have_gitlab_http_status(:ok) @@ -167,7 +172,7 @@ RSpec.describe API::Labels do it 'returns 204 for existing label (rest route)' do label_id = spec_params[:name] || spec_params[:label_id] - delete api("/projects/#{project.id}/labels/#{label_id}", user), params: spec_params.except(:name, :label_id) + delete api("/projects/#{project.id}/labels/#{ERB::Util.url_encode(label_id)}", user), params: spec_params.except(:name, :label_id) expect(response).to have_gitlab_http_status(:no_content) end @@ -179,7 +184,7 @@ RSpec.describe API::Labels do describe 'GET /projects/:id/labels' do let_it_be(:group) { create(:group) } - let_it_be(:group_label) { create(:group_label, title: 'feature label', group: group) } + let_it_be(:group_label) { create(:group_label, title: valid_group_label_title_1, group: group) } before do project.update!(group: group) @@ -219,7 +224,7 @@ RSpec.describe API::Labels do 'closed_issues_count' => 1, 'open_merge_requests_count' => 0, 'name' => label1.name, - 'description' => 'the best label', + 'description' => label1.description, 'color' => a_string_matching(/^#\h{6}$/), 'text_color' => a_string_matching(/^#\h{6}$/), 'priority' => nil, @@ -293,14 +298,14 @@ RSpec.describe API::Labels do it 'returns created label when all params' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo', + name: valid_label_title_2, color: '#FFAABB', description: 'test', priority: 2 } expect(response).to have_gitlab_http_status(:created) - expect(json_response['name']).to eq('Foo') + expect(json_response['name']).to eq(valid_label_title_2) expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to eq('test') expect(json_response['priority']).to eq(2) @@ -309,12 +314,12 @@ RSpec.describe API::Labels do it 'returns created label when only required params' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo & Bar', + name: valid_label_title_2, color: '#FFAABB' } expect(response).to have_gitlab_http_status(:created) - expect(json_response['name']).to eq('Foo & Bar') + expect(json_response['name']).to eq(valid_label_title_2) expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to be_nil expect(json_response['priority']).to be_nil @@ -323,13 +328,13 @@ RSpec.describe API::Labels do it 'creates a prioritized label' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo & Bar', + name: valid_label_title_2, color: '#FFAABB', priority: 3 } expect(response).to have_gitlab_http_status(:created) - expect(json_response['name']).to eq('Foo & Bar') + expect(json_response['name']).to eq(valid_label_title_2) expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to be_nil expect(json_response['priority']).to eq(3) @@ -348,7 +353,7 @@ RSpec.describe API::Labels do it 'returns 400 for invalid color' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo', + name: valid_label_title_2, color: '#FFAA' } expect(response).to have_gitlab_http_status(:bad_request) @@ -358,7 +363,7 @@ RSpec.describe API::Labels do it 'returns 400 for too long color code' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo', + name: valid_label_title_2, color: '#FFAAFFFF' } expect(response).to have_gitlab_http_status(:bad_request) @@ -393,7 +398,7 @@ RSpec.describe API::Labels do it 'returns 400 for invalid priority' do post api("/projects/#{project.id}/labels", user), params: { - name: 'Foo', + name: valid_label_title_2, color: '#FFAAFFFF', priority: 'foo' } @@ -404,7 +409,7 @@ RSpec.describe API::Labels do it 'returns 409 if label already exists in project' do post api("/projects/#{project.id}/labels", user), params: { - name: 'label1', + name: valid_label_title_1, color: '#FFAABB' } expect(response).to have_gitlab_http_status(:conflict) @@ -414,7 +419,7 @@ RSpec.describe API::Labels do describe 'DELETE /projects/:id/labels' do it_behaves_like 'label delete API' do - let(:spec_params) { { name: 'label1' } } + let(:spec_params) { { name: valid_label_title_1 } } end it_behaves_like 'label delete API' do @@ -422,7 +427,7 @@ RSpec.describe API::Labels do end it 'returns 404 for non existing label' do - delete api("/projects/#{project.id}/labels", user), params: { name: 'label2' } + delete api("/projects/#{project.id}/labels", user), params: { name: 'unknown' } expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Label Not Found') @@ -446,14 +451,14 @@ RSpec.describe API::Labels do it_behaves_like '412 response' do let(:request) { api("/projects/#{project.id}/labels", user) } - let(:params) { { name: 'label1' } } + let(:params) { { name: valid_label_title_1 } } end end describe 'PUT /projects/:id/labels' do context 'when using name' do it_behaves_like 'label update API' do - let(:spec_params) { { name: 'label1' } } + let(:spec_params) { { name: valid_label_title_1 } } let(:expected_response_label_id) { label1.id } end end @@ -468,7 +473,7 @@ RSpec.describe API::Labels do it 'returns 404 if label does not exist' do put api("/projects/#{project.id}/labels", user), params: { - name: 'label2', + name: valid_label_title_2, new_name: 'label3' } @@ -571,7 +576,7 @@ RSpec.describe API::Labels do describe "POST /projects/:id/labels/:label_id/subscribe" do context "when label_id is a label title" do it "subscribes to the label" do - post api("/projects/#{project.id}/labels/#{label1.title}/subscribe", user) + post api("/projects/#{project.id}/labels/#{valid_label_title_1_esc}/subscribe", user) expect(response).to have_gitlab_http_status(:created) expect(json_response["name"]).to eq(label1.title) @@ -617,7 +622,7 @@ RSpec.describe API::Labels do context "when label_id is a label title" do it "unsubscribes from the label" do - post api("/projects/#{project.id}/labels/#{label1.title}/unsubscribe", user) + post api("/projects/#{project.id}/labels/#{valid_label_title_1_esc}/unsubscribe", user) expect(response).to have_gitlab_http_status(:created) expect(json_response["name"]).to eq(label1.title) diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index e5d11fb1218..5c85909a851 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -32,6 +32,7 @@ RSpec.describe API::MavenPackages do end let(:version) { '1.0-SNAPSHOT' } + let(:param_path) { "#{package_name}/#{version}"} before do project.add_developer(user) @@ -695,6 +696,14 @@ RSpec.describe API::MavenPackages do expect(json_response['message']).to include('Duplicate package is not allowed') end + context 'when uploading to the versionless package which contains metadata about all versions' do + let(:version) { nil } + let(:param_path) { package_name } + let!(:package) { create(:maven_package, project: project, version: version, name: project.full_path) } + + it_behaves_like 'storing the package file' + end + context 'when uploading different non-duplicate files to the same package' do let!(:package) { create(:maven_package, project: project, name: project.full_path) } @@ -744,7 +753,7 @@ RSpec.describe API::MavenPackages do end def upload_file(params: {}, request_headers: headers, file_extension: 'jar') - url = "/projects/#{project.id}/packages/maven/#{package_name}/#{version}/my-app-1.0-20180724.124855-1.#{file_extension}" + url = "/projects/#{project.id}/packages/maven/#{param_path}/my-app-1.0-20180724.124855-1.#{file_extension}" workhorse_finalize( api(url), method: :put, diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 3a3eae73932..1d7476d6f23 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1542,9 +1542,9 @@ RSpec.describe API::MergeRequests do end end - context 'when access_raw_diffs is passed as an option' do + context 'when access_raw_diffs is true' do it_behaves_like 'accesses diffs via raw_diffs' do - let(:params) { { access_raw_diffs: true } } + let(:params) { { access_raw_diffs: "true" } } end end end @@ -1736,6 +1736,36 @@ RSpec.describe API::MergeRequests do end end + context 'accepts reviewer_ids' do + let(:params) do + { + title: 'Test merge request', + source_branch: 'feature_conflict', + target_branch: 'master', + author_id: user.id, + reviewer_ids: [user2.id] + } + end + + it 'creates a new merge request with a reviewer' do + post api("/projects/#{project.id}/merge_requests", user), params: params + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['title']).to eq('Test merge request') + expect(json_response['reviewers'].first['name']).to eq(user2.name) + end + + it 'creates a new merge request with no reviewer' do + params[:reviewer_ids] = [] + + post api("/projects/#{project.id}/merge_requests", user), params: params + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['title']).to eq('Test merge request') + expect(json_response['reviewers']).to be_empty + end + end + context 'between branches projects' do context 'different labels' do let(:params) do @@ -2081,6 +2111,34 @@ RSpec.describe API::MergeRequests do it_behaves_like 'issuable update endpoint' do let(:entity) { merge_request } end + + context 'accepts reviewer_ids' do + let(:params) do + { + title: 'Updated merge request', + reviewer_ids: [user2.id] + } + end + + it 'adds a reviewer to the existing merge request' do + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['title']).to eq('Updated merge request') + expect(json_response['reviewers'].first['name']).to eq(user2.name) + end + + it 'removes a reviewer from the existing merge request' do + merge_request.reviewers = [user2] + params[:reviewer_ids] = [] + + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['title']).to eq('Updated merge request') + expect(json_response['reviewers']).to be_empty + end + end end describe "POST /projects/:id/merge_requests/:merge_request_iid/context_commits" do diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb index 23d5df873d4..52c7408545f 100644 --- a/spec/requests/api/oauth_tokens_spec.rb +++ b/spec/requests/api/oauth_tokens_spec.rb @@ -26,17 +26,14 @@ RSpec.describe 'OAuth tokens' do end context 'when user does not have 2FA enabled' do - # NOTE: using ROPS grant flow without client credentials will be deprecated - # and removed in the next version of Doorkeeper. - # See https://gitlab.com/gitlab-org/gitlab/-/issues/219137 context 'when no client credentials provided' do - it 'creates an access token' do + it 'does not create an access token' do user = create(:user) request_oauth_token(user) - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['access_token']).not_to be_nil + expect(response).to have_gitlab_http_status(:unauthorized) + expect(json_response['access_token']).to be_nil end end @@ -54,15 +51,11 @@ RSpec.describe 'OAuth tokens' do context 'with invalid credentials' do it 'does not create an access token' do - # NOTE: remove this after update to Doorkeeper 5.5 or newer, see - # https://gitlab.com/gitlab-org/gitlab/-/issues/219137 - pending 'Enable this example after upgrading Doorkeeper to 5.5 or newer' - user = create(:user) request_oauth_token(user, basic_auth_header(client.uid, 'invalid secret')) - expect(response).to have_gitlab_http_status(:bad_request) + expect(response).to have_gitlab_http_status(:unauthorized) expect(json_response['error']).to eq('invalid_client') end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 4acd0eea448..6d8cdde2c4f 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1139,7 +1139,7 @@ RSpec.describe API::Projects do let!(:public_project) { create(:project, :public, name: 'public_project', creator_id: user4.id, namespace: user4.namespace) } it 'returns error when user not found' do - get api('/users/0/projects/') + get api("/users/#{non_existing_record_id}/projects/") expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 User Not Found') @@ -1687,7 +1687,7 @@ RSpec.describe API::Projects do end it 'returns a 404 error if not found' do - get api('/projects/42', user) + get api("/projects/#{non_existing_record_id}", user) expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Project Not Found') end @@ -2048,7 +2048,7 @@ RSpec.describe API::Projects do end it 'returns a 404 error if not found' do - get api('/projects/42/users', user) + get api("/projects/#{non_existing_record_id}/users", user) expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Project Not Found') @@ -2154,7 +2154,7 @@ RSpec.describe API::Projects do end it 'fails if forked_from project which does not exist' do - post api("/projects/#{project_fork_target.id}/fork/0", admin) + post api("/projects/#{project_fork_target.id}/fork/#{non_existing_record_id}", admin) expect(response).to have_gitlab_http_status(:not_found) end @@ -2398,7 +2398,7 @@ RSpec.describe API::Projects do end it 'returns a 404 error when project does not exist' do - delete api("/projects/123/share/#{non_existing_record_id}", user) + delete api("/projects/#{non_existing_record_id}/share/#{non_existing_record_id}", user) expect(response).to have_gitlab_http_status(:not_found) end @@ -2955,7 +2955,7 @@ RSpec.describe API::Projects do end it 'returns the proper security headers' do - get api('/projects/1/starrers', current_user) + get api("/projects/#{public_project.id}/starrers", current_user) expect(response).to include_security_headers end @@ -3028,7 +3028,7 @@ RSpec.describe API::Projects do end it 'returns not_found(404) for not existing project' do - get api("/projects/0/languages", user) + get api("/projects/#{non_existing_record_id}/languages", user) expect(response).to have_gitlab_http_status(:not_found) end @@ -3079,7 +3079,7 @@ RSpec.describe API::Projects do end it 'does not remove a non existing project' do - delete api('/projects/1328', user) + delete api("/projects/#{non_existing_record_id}", user) expect(response).to have_gitlab_http_status(:not_found) end @@ -3098,7 +3098,7 @@ RSpec.describe API::Projects do end it 'does not remove a non existing project' do - delete api('/projects/1328', admin) + delete api("/projects/#{non_existing_record_id}", admin) expect(response).to have_gitlab_http_status(:not_found) end @@ -3177,7 +3177,7 @@ RSpec.describe API::Projects do end it 'fails if project to fork from does not exist' do - post api('/projects/424242/fork', user) + post api("/projects/#{non_existing_record_id}/fork", user) expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Project Not Found') @@ -3211,7 +3211,7 @@ RSpec.describe API::Projects do end it 'fails if trying to fork to non-existent namespace' do - post api("/projects/#{project.id}/fork", user2), params: { namespace: 42424242 } + post api("/projects/#{project.id}/fork", user2), params: { namespace: non_existing_record_id } expect(response).to have_gitlab_http_status(:not_found) expect(json_response['message']).to eq('404 Namespace Not Found') @@ -3328,8 +3328,8 @@ RSpec.describe API::Projects do expect(json_response['message']['path']).to eq(['has already been taken']) end - it 'accepts a name for the target project' do - post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project' } + it 'accepts custom parameters for the target project' do + post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project', description: 'A description', visibility: 'private' } expect(response).to have_gitlab_http_status(:created) expect(json_response['name']).to eq('My Random Project') @@ -3337,6 +3337,8 @@ RSpec.describe API::Projects do expect(json_response['owner']['id']).to eq(user2.id) expect(json_response['namespace']['id']).to eq(user2.namespace.id) expect(json_response['forked_from_project']['id']).to eq(project.id) + expect(json_response['description']).to eq('A description') + expect(json_response['visibility']).to eq('private') expect(json_response['import_status']).to eq('scheduled') expect(json_response).to include("import_error") end @@ -3368,6 +3370,13 @@ RSpec.describe API::Projects do expect(json_response['message']['path']).to eq(['has already been taken']) expect(json_response['message']['name']).to eq(['has already been taken']) end + + it 'fails to fork with an unknown visibility level' do + post api("/projects/#{project.id}/fork", user2), params: { visibility: 'something' } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('visibility does not have a valid value') + end end context 'when unauthenticated' do diff --git a/spec/requests/api/pypi_packages_spec.rb b/spec/requests/api/pypi_packages_spec.rb index 72a470dca4b..94ecd177890 100644 --- a/spec/requests/api/pypi_packages_spec.rb +++ b/spec/requests/api/pypi_packages_spec.rb @@ -5,6 +5,7 @@ RSpec.describe API::PypiPackages do include WorkhorseHelpers include PackagesManagerApiSpecHelpers include HttpBasicAuthHelpers + using RSpec::Parameterized::TableSyntax let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project, :public) } @@ -20,8 +21,6 @@ RSpec.describe API::PypiPackages do subject { get api(url) } context 'with valid project' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'PyPI package versions' | :success 'PUBLIC' | :guest | true | true | 'PyPI package versions' | :success @@ -83,8 +82,6 @@ RSpec.describe API::PypiPackages do subject { post api(url), headers: headers } context 'with valid project' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'process PyPI api request' | :success 'PUBLIC' | :guest | true | true | 'process PyPI api request' | :forbidden @@ -149,8 +146,6 @@ RSpec.describe API::PypiPackages do end context 'with valid project' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'PyPI package creation' | :created 'PUBLIC' | :guest | true | true | 'process PyPI api request' | :forbidden @@ -239,8 +234,6 @@ RSpec.describe API::PypiPackages do subject { get api(url) } context 'with valid project' do - using RSpec::Parameterized::TableSyntax - where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do 'PUBLIC' | :developer | true | true | 'PyPI package download' | :success 'PUBLIC' | :guest | true | true | 'PyPI package download' | :success diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 45bce8c8a5c..a05f1730708 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -610,4 +610,102 @@ RSpec.describe API::Repositories do end end end + + describe 'POST /projects/:id/repository/changelog' do + context 'when the changelog_api feature flag is enabled' do + it 'generates the changelog for a version' do + spy = instance_spy(Repositories::ChangelogService) + + allow(Repositories::ChangelogService) + .to receive(:new) + .with( + project, + user, + version: '1.0.0', + from: 'foo', + to: 'bar', + date: DateTime.new(2020, 1, 1), + branch: 'kittens', + trailer: 'Foo', + file: 'FOO.md', + message: 'Commit message' + ) + .and_return(spy) + + allow(spy).to receive(:execute) + + post( + api("/projects/#{project.id}/repository/changelog", user), + params: { + version: '1.0.0', + from: 'foo', + to: 'bar', + date: '2020-01-01', + branch: 'kittens', + trailer: 'Foo', + file: 'FOO.md', + message: 'Commit message' + } + ) + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'produces an error when generating the changelog fails' do + spy = instance_spy(Repositories::ChangelogService) + + allow(Repositories::ChangelogService) + .to receive(:new) + .with( + project, + user, + version: '1.0.0', + from: 'foo', + to: 'bar', + date: DateTime.new(2020, 1, 1), + branch: 'kittens', + trailer: 'Foo', + file: 'FOO.md', + message: 'Commit message' + ) + .and_return(spy) + + allow(spy) + .to receive(:execute) + .and_raise(Gitlab::Changelog::Committer::CommitError.new('oops')) + + post( + api("/projects/#{project.id}/repository/changelog", user), + params: { + version: '1.0.0', + from: 'foo', + to: 'bar', + date: '2020-01-01', + branch: 'kittens', + trailer: 'Foo', + file: 'FOO.md', + message: 'Commit message' + } + ) + + expect(response).to have_gitlab_http_status(:internal_server_error) + expect(json_response['message']).to eq('Failed to generate the changelog: oops') + end + end + + context 'when the changelog_api feature flag is disabled' do + before do + stub_feature_flags(changelog_api: false) + end + + it 'responds with a 404 Not Found' do + post( + api("/projects/#{project.id}/repository/changelog", user), + params: { version: '1.0.0', from: 'foo', to: 'bar' } + ) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end end diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb new file mode 100644 index 00000000000..9fd7eb2177d --- /dev/null +++ b/spec/requests/api/resource_access_tokens_spec.rb @@ -0,0 +1,293 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe API::ResourceAccessTokens do + context "when the resource is a project" do + let_it_be(:project) { create(:project) } + let_it_be(:other_project) { create(:project) } + let_it_be(:user) { create(:user) } + + describe "GET projects/:id/access_tokens" do + subject(:get_tokens) { get api("/projects/#{project_id}/access_tokens", user) } + + context "when the user has maintainer permissions" do + let_it_be(:project_bot) { create(:user, :project_bot) } + let_it_be(:access_tokens) { create_list(:personal_access_token, 3, user: project_bot) } + let_it_be(:project_id) { project.id } + + before do + project.add_maintainer(user) + project.add_maintainer(project_bot) + end + + it "gets a list of access tokens for the specified project" do + get_tokens + + token_ids = json_response.map { |token| token['id'] } + + expect(response).to have_gitlab_http_status(:ok) + expect(token_ids).to match_array(access_tokens.pluck(:id)) + end + + context "when using a project access token to GET other project access tokens" do + let_it_be(:token) { access_tokens.first } + + it "gets a list of access tokens for the specified project" do + get api("/projects/#{project_id}/access_tokens", personal_access_token: token) + + token_ids = json_response.map { |token| token['id'] } + + expect(response).to have_gitlab_http_status(:ok) + expect(token_ids).to match_array(access_tokens.pluck(:id)) + end + end + + context "when tokens belong to a different project" do + let_it_be(:bot) { create(:user, :project_bot) } + let_it_be(:token) { create(:personal_access_token, user: bot) } + + before do + other_project.add_maintainer(bot) + other_project.add_maintainer(user) + end + + it "does not return tokens from a different project" do + get_tokens + + token_ids = json_response.map { |token| token['id'] } + + expect(token_ids).not_to include(token.id) + end + end + + context "when the project has no access tokens" do + let(:project_id) { other_project.id } + + before do + other_project.add_maintainer(user) + end + + it 'returns an empty array' do + get_tokens + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to eq([]) + end + end + + context "when trying to get the tokens of a different project" do + let_it_be(:project_id) { other_project.id } + + it "returns 404" do + get_tokens + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context "when the project does not exist" do + let(:project_id) { non_existing_record_id } + + it "returns 404" do + get_tokens + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + context "when the user does not have valid permissions" do + let_it_be(:project_bot) { create(:user, :project_bot) } + let_it_be(:access_tokens) { create_list(:personal_access_token, 3, user: project_bot) } + let_it_be(:project_id) { project.id } + + before do + project.add_developer(user) + project.add_maintainer(project_bot) + end + + it "returns 401" do + get_tokens + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + end + + describe "DELETE projects/:id/access_tokens/:token_id", :sidekiq_inline do + subject(:delete_token) { delete api("/projects/#{project_id}/access_tokens/#{token_id}", user) } + + let_it_be(:project_bot) { create(:user, :project_bot) } + let_it_be(:token) { create(:personal_access_token, user: project_bot) } + let_it_be(:project_id) { project.id } + let_it_be(:token_id) { token.id } + + before do + project.add_maintainer(project_bot) + end + + context "when the user has maintainer permissions" do + before do + project.add_maintainer(user) + end + + it "deletes the project access token from the project" do + delete_token + + expect(response).to have_gitlab_http_status(:no_content) + expect(User.exists?(project_bot.id)).to be_falsy + end + + context "when attempting to delete a non-existent project access token" do + let_it_be(:token_id) { non_existing_record_id } + + it "does not delete the token, and returns 404" do + delete_token + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to include("Could not find project access token with token_id: #{token_id}") + end + end + + context "when attempting to delete a token that does not belong to the specified project" do + let_it_be(:project_id) { other_project.id } + + before do + other_project.add_maintainer(user) + end + + it "does not delete the token, and returns 404" do + delete_token + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to include("Could not find project access token with token_id: #{token_id}") + end + end + end + + context "when the user does not have valid permissions" do + before do + project.add_developer(user) + end + + it "does not delete the token, and returns 400", :aggregate_failures do + delete_token + + expect(response).to have_gitlab_http_status(:bad_request) + expect(User.exists?(project_bot.id)).to be_truthy + expect(response.body).to include("#{user.name} cannot delete #{token.user.name}") + end + end + end + + describe "POST projects/:id/access_tokens" do + let_it_be(:params) { { name: "test", scopes: ["api"], expires_at: Date.today + 1.month } } + + subject(:create_token) { post api("/projects/#{project_id}/access_tokens", user), params: params } + + context "when the user has maintainer permissions" do + let_it_be(:project_id) { project.id } + let_it_be(:expires_at) { 1.month.from_now } + + before do + project.add_maintainer(user) + end + + context "with valid params" do + context "with full params" do + it "creates a project access token with the params", :aggregate_failures do + create_token + + expect(response).to have_gitlab_http_status(:created) + expect(json_response["name"]).to eq("test") + expect(json_response["scopes"]).to eq(["api"]) + expect(json_response["expires_at"]).to eq(expires_at.to_date.iso8601) + end + end + + context "when 'expires_at' is not set" do + let_it_be(:params) { { name: "test", scopes: ["api"] } } + + it "creates a project access token with the params", :aggregate_failures do + create_token + + expect(response).to have_gitlab_http_status(:created) + expect(json_response["name"]).to eq("test") + expect(json_response["scopes"]).to eq(["api"]) + expect(json_response["expires_at"]).to eq(nil) + end + end + end + + context "with invalid params" do + context "when missing the 'name' param" do + let_it_be(:params) { { scopes: ["api"], expires_at: 5.days.from_now } } + + it "does not create a project access token without 'name'" do + create_token + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to include("name is missing") + end + end + + context "when missing the 'scopes' param" do + let_it_be(:params) { { name: "test", expires_at: 5.days.from_now } } + + it "does not create a project access token without 'scopes'" do + create_token + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to include("scopes is missing") + end + end + end + + context "when trying to create a token in a different project" do + let_it_be(:project_id) { other_project.id } + + it "does not create the token, and returns the project not found error" do + create_token + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to include("Project Not Found") + end + end + end + + context "when the user does not have valid permissions" do + let_it_be(:project_id) { project.id } + + context "when the user is a developer" do + before do + project.add_developer(user) + end + + it "does not create the token, and returns the permission error" do + create_token + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to include("User does not have permission to create project access token") + end + end + + context "when a project access token tries to create another project access token" do + let_it_be(:project_bot) { create(:user, :project_bot) } + let_it_be(:user) { project_bot } + + before do + project.add_maintainer(user) + end + + it "does not allow a project access token to create another project access token" do + create_token + + expect(response).to have_gitlab_http_status(:bad_request) + expect(response.body).to include("User does not have permission to create project access token") + end + end + end + end + end +end diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index 8fb0f8fc51a..3b84c812010 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -199,6 +199,14 @@ RSpec.describe API::Settings, 'Settings' do expect(json_response['allow_local_requests_from_hooks_and_services']).to eq(true) end + it 'supports legacy asset_proxy_whitelist' do + put api("/application/settings", admin), + params: { asset_proxy_whitelist: ['example.com', '*.example.com'] } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['asset_proxy_allowlist']).to eq(['example.com', '*.example.com', 'localhost']) + end + it 'disables ability to switch to legacy storage' do put api("/application/settings", admin), params: { hashed_storage_enabled: false } @@ -362,24 +370,24 @@ RSpec.describe API::Settings, 'Settings' do asset_proxy_enabled: true, asset_proxy_url: 'http://assets.example.com', asset_proxy_secret_key: 'shared secret', - asset_proxy_whitelist: ['example.com', '*.example.com'] + asset_proxy_allowlist: ['example.com', '*.example.com'] } expect(response).to have_gitlab_http_status(:ok) expect(json_response['asset_proxy_enabled']).to be(true) expect(json_response['asset_proxy_url']).to eq('http://assets.example.com') expect(json_response['asset_proxy_secret_key']).to be_nil - expect(json_response['asset_proxy_whitelist']).to eq(['example.com', '*.example.com', 'localhost']) + expect(json_response['asset_proxy_allowlist']).to eq(['example.com', '*.example.com', 'localhost']) end - it 'allows a string for asset_proxy_whitelist' do + it 'allows a string for asset_proxy_allowlist' do put api('/application/settings', admin), params: { - asset_proxy_whitelist: 'example.com, *.example.com' + asset_proxy_allowlist: 'example.com, *.example.com' } expect(response).to have_gitlab_http_status(:ok) - expect(json_response['asset_proxy_whitelist']).to eq(['example.com', '*.example.com', 'localhost']) + expect(json_response['asset_proxy_allowlist']).to eq(['example.com', '*.example.com', 'localhost']) end end diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb index 78a2688ac5e..7f53d379af5 100644 --- a/spec/requests/api/suggestions_spec.rb +++ b/spec/requests/api/suggestions_spec.rb @@ -65,6 +65,19 @@ RSpec.describe API::Suggestions do end end + context 'when a custom commit message is included' do + it 'renders an ok response and returns json content' do + project.add_maintainer(user) + + message = "cool custom commit message!" + + put api(url, user), params: { commit_message: message } + + expect(response).to have_gitlab_http_status(:ok) + expect(project.repository.commit.message).to eq(message) + end + end + context 'when not able to apply patch' do let(:url) { "/suggestions/#{unappliable_suggestion.id}/apply" } @@ -113,9 +126,11 @@ RSpec.describe API::Suggestions do let(:url) { "/suggestions/batch_apply" } context 'when successfully applies multiple patches as a batch' do - it 'renders an ok response and returns json content' do + before do project.add_maintainer(user) + end + it 'renders an ok response and returns json content' do put api(url, user), params: { ids: [suggestion.id, suggestion2.id] } expect(response).to have_gitlab_http_status(:ok) @@ -123,6 +138,16 @@ RSpec.describe API::Suggestions do 'appliable', 'applied', 'from_content', 'to_content')) end + + it 'provides a custom commit message' do + message = "cool custom commit message!" + + put api(url, user), params: { ids: [suggestion.id, suggestion2.id], + commit_message: message } + + expect(response).to have_gitlab_http_status(:ok) + expect(project.repository.commit.message).to eq(message) + end end context 'when not able to apply one or more of the patches' do diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index e1c5bfd82c4..adb37c62dc3 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -65,7 +65,9 @@ RSpec.describe API::Templates do expect(json_response['nickname']).to be_nil expect(json_response['popular']).to be true expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') - expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') + # This was dropped: + # https://github.com/github/choosealicense.com/commit/325806b42aa3d5b78e84120327ec877bc936dbdd#diff-66df8f1997786f7052d29010f2cbb4c66391d60d24ca624c356acc0ab986f139 + expect(json_response['source_url']).to be_nil expect(json_response['description']).to include('A short and simple permissive license with conditions') expect(json_response['conditions']).to eq(%w[include-copyright]) expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) @@ -81,7 +83,7 @@ RSpec.describe API::Templates do expect(response).to have_gitlab_http_status(:ok) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(12) + expect(json_response.size).to eq(13) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 94fba451860..66d6c29cd4d 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -2565,7 +2565,7 @@ RSpec.describe API::Users do it 'does not approve a deactivated user' do expect { approve }.not_to change { deactivated_user.reload.state } expect(response).to have_gitlab_http_status(:conflict) - expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval') + expect(json_response['message']).to eq('The user you are trying to approve is not pending approval') end end @@ -2585,7 +2585,7 @@ RSpec.describe API::Users do it 'returns 201' do expect { approve }.not_to change { user.reload.state } expect(response).to have_gitlab_http_status(:conflict) - expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval') + expect(json_response['message']).to eq('The user you are trying to approve is not pending approval') end end @@ -2595,7 +2595,7 @@ RSpec.describe API::Users do it 'returns 403' do expect { approve }.not_to change { blocked_user.reload.state } expect(response).to have_gitlab_http_status(:conflict) - expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval') + expect(json_response['message']).to eq('The user you are trying to approve is not pending approval') end end @@ -2605,7 +2605,7 @@ RSpec.describe API::Users do it 'returns 403' do expect { approve }.not_to change { ldap_blocked_user.reload.state } expect(response).to have_gitlab_http_status(:conflict) - expect(json_response['message']).to eq('The user you are trying to approve is not pending an approval') + expect(json_response['message']).to eq('The user you are trying to approve is not pending approval') end end diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb index a0a0f66c8d1..7abbaf4f9ec 100644 --- a/spec/requests/api/version_spec.rb +++ b/spec/requests/api/version_spec.rb @@ -33,6 +33,12 @@ RSpec.describe API::Version do expect_version end + + it 'returns "200" response on head requests' do + head api('/version', personal_access_token: personal_access_token) + + expect(response).to have_gitlab_http_status(:ok) + end end context 'with read_user scope' do @@ -43,6 +49,12 @@ RSpec.describe API::Version do expect_version end + + it 'returns "200" response on head requests' do + head api('/version', personal_access_token: personal_access_token) + + expect(response).to have_gitlab_http_status(:ok) + end end context 'with neither api nor read_user scope' do diff --git a/spec/requests/groups/email_campaigns_controller_spec.rb b/spec/requests/groups/email_campaigns_controller_spec.rb new file mode 100644 index 00000000000..930e645f6c0 --- /dev/null +++ b/spec/requests/groups/email_campaigns_controller_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Groups::EmailCampaignsController do + include InProductMarketingHelper + using RSpec::Parameterized::TableSyntax + + describe 'GET #index', :snowplow do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:user) { create(:user) } + let(:track) { 'create' } + let(:series) { '0' } + let(:schema) { described_class::EMAIL_CAMPAIGNS_SCHEMA_URL } + let(:data) do + { + namespace_id: group.id, + track: track.to_sym, + series: series.to_i, + subject_line: subject_line(track.to_sym, series.to_i) + } + end + + before do + sign_in(user) + group.add_developer(user) + allow(Gitlab::Tracking).to receive(:self_describing_event) + end + + subject do + get group_email_campaigns_url(group, track: track, series: series) + response + end + + shared_examples 'track and redirect' do + it do + is_expected.to track_self_describing_event(schema, data) + is_expected.to have_gitlab_http_status(:redirect) + end + end + + shared_examples 'no track and 404' do + it do + is_expected.not_to track_self_describing_event + is_expected.to have_gitlab_http_status(:not_found) + end + end + + describe 'track parameter' do + context 'when valid' do + where(track: Namespaces::InProductMarketingEmailsService::TRACKS.keys) + + with_them do + it_behaves_like 'track and redirect' + end + end + + context 'when invalid' do + where(track: [nil, 'xxxx']) + + with_them do + it_behaves_like 'no track and 404' + end + end + end + + describe 'series parameter' do + context 'when valid' do + where(series: (0..Namespaces::InProductMarketingEmailsService::INTERVAL_DAYS.length - 1).to_a) + + with_them do + it_behaves_like 'track and redirect' + end + end + + context 'when invalid' do + where(series: [-1, nil, Namespaces::InProductMarketingEmailsService::INTERVAL_DAYS.length]) + + with_them do + it_behaves_like 'no track and 404' + end + end + end + end +end diff --git a/spec/requests/oauth/tokens_controller_spec.rb b/spec/requests/oauth/tokens_controller_spec.rb new file mode 100644 index 00000000000..c3cdae2cd21 --- /dev/null +++ b/spec/requests/oauth/tokens_controller_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Oauth::TokensController do + it 'allows cross-origin POST requests' do + post '/oauth/token', headers: { 'Origin' => 'http://notgitlab.com' } + + expect(response.headers['Access-Control-Allow-Origin']).to eq '*' + expect(response.headers['Access-Control-Allow-Methods']).to eq 'POST' + expect(response.headers['Access-Control-Allow-Headers']).to be_nil + expect(response.headers['Access-Control-Allow-Credentials']).to be_nil + end +end diff --git a/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb new file mode 100644 index 00000000000..5d2f3e98bb4 --- /dev/null +++ b/spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Projects::Ci::PrometheusMetrics::HistogramsController' do + let_it_be(:project) { create(:project, :public) } + + describe 'POST /*namespace_id/:project_id/-/ci/prometheus_metrics/histograms' do + context 'with known histograms' do + it 'returns 201 Created' do + post histograms_route(histograms: [ + { name: :pipeline_graph_link_calculation_duration_seconds, value: 1 }, + { name: :pipeline_graph_links_total, value: 10 } + ]) + + expect(response).to have_gitlab_http_status(:created) + end + end + + context 'with unknown histograms' do + it 'returns 404 Not Found' do + post histograms_route(histograms: [{ name: :chunky_bacon, value: 5 }]) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'with the feature flag disabled' do + before do + stub_feature_flags(ci_accept_frontend_prometheus_metrics: false) + end + + it 'returns 202 Accepted' do + post histograms_route(histograms: [ + { name: :pipeline_graph_link_calculation_duration_seconds, value: 1 } + ]) + + expect(response).to have_gitlab_http_status(:accepted) + end + end + end + + def histograms_route(params = {}) + namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, **params) + end +end diff --git a/spec/requests/projects/noteable_notes_spec.rb b/spec/requests/projects/noteable_notes_spec.rb index 2bf1ffb2edc..5ae2aadaa84 100644 --- a/spec/requests/projects/noteable_notes_spec.rb +++ b/spec/requests/projects/noteable_notes_spec.rb @@ -18,9 +18,7 @@ RSpec.describe 'Project noteable notes' do login_as(user) end - it 'does not set a Gitlab::EtagCaching ETag if there is a note' do - create(:note_on_merge_request, noteable: merge_request, project: merge_request.project) - + it 'does not set a Gitlab::EtagCaching ETag' do get notes_path expect(response).to have_gitlab_http_status(:ok) @@ -29,12 +27,5 @@ RSpec.describe 'Project noteable notes' do # interfere with notes pagination expect(response_etag).not_to eq(stored_etag) end - - it 'sets a Gitlab::EtagCaching ETag if there is no note' do - get notes_path - - expect(response).to have_gitlab_http_status(:ok) - expect(response_etag).to eq(stored_etag) - end end end diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index 34f34c0b850..1bb260b5ea1 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Rack Attack global throttles' do +RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching do include RackAttackSpecHelpers let(:settings) { Gitlab::CurrentSettings.current_application_settings } @@ -149,14 +149,14 @@ RSpec.describe 'Rack Attack global throttles' do expect(response).to have_gitlab_http_status(:ok) end - arguments = { + arguments = a_hash_including({ message: 'Rack_Attack', env: :throttle, remote_ip: '127.0.0.1', request_method: 'GET', path: '/users/sign_in', matched: 'throttle_unauthenticated' - } + }) expect(Gitlab::AuthLogger).to receive(:error).with(arguments) diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index b224ef87229..f3ddcbef1c2 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -268,6 +268,14 @@ RSpec.describe UsersController do end it_behaves_like 'renders all public keys' + + context 'when public visibility is restricted' do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it_behaves_like 'renders all public keys' + end end end end diff --git a/spec/requests/whats_new_controller_spec.rb b/spec/requests/whats_new_controller_spec.rb index 8005d38dbb0..b17fa7e4a26 100644 --- a/spec/requests/whats_new_controller_spec.rb +++ b/spec/requests/whats_new_controller_spec.rb @@ -7,59 +7,39 @@ RSpec.describe WhatsNewController do let(:item) { double(:item) } let(:highlights) { double(:highlight, items: [item], map: [item].map, next_page: 2) } - context 'with whats_new_drawer feature enabled' do - before do - stub_feature_flags(whats_new_drawer: true) - end - - context 'with no page param' do - it 'responds with paginated data and headers' do - allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(highlights) - allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item) + context 'with no page param' do + it 'responds with paginated data and headers' do + allow(ReleaseHighlight).to receive(:paginated).with(page: 1).and_return(highlights) - get whats_new_path, xhr: true + get whats_new_path, xhr: true - expect(response.body).to eq(highlights.items.to_json) - expect(response.headers['X-Next-Page']).to eq(2) - end + expect(response.body).to eq(highlights.items.to_json) + expect(response.headers['X-Next-Page']).to eq(2) end + end - context 'with page param' do - it 'passes the page parameter' do - expect(ReleaseHighlight).to receive(:paginated).with(page: 2).and_call_original - - get whats_new_path(page: 2), xhr: true - end - - it 'returns a 404 if page param is negative' do - get whats_new_path(page: -1), xhr: true + context 'with page param' do + it 'passes the page parameter' do + expect(ReleaseHighlight).to receive(:paginated).with(page: 2).and_call_original - expect(response).to have_gitlab_http_status(:not_found) - end + get whats_new_path(page: 2), xhr: true end - context 'with version param' do - it 'returns items without pagination headers' do - allow(ReleaseHighlight).to receive(:for_version).with(version: '42').and_return(highlights) - allow(Gitlab::WhatsNew::ItemPresenter).to receive(:present).with(item).and_return(item) + it 'returns a 404 if page param is negative' do + get whats_new_path(page: -1), xhr: true - get whats_new_path(version: 42), xhr: true - - expect(response.body).to eq(highlights.items.to_json) - expect(response.headers['X-Next-Page']).to be_nil - end + expect(response).to have_gitlab_http_status(:not_found) end end - context 'with whats_new_drawer feature disabled' do - before do - stub_feature_flags(whats_new_drawer: false) - end + context 'with version param' do + it 'returns items without pagination headers' do + allow(ReleaseHighlight).to receive(:for_version).with(version: '42').and_return(highlights) - it 'returns a 404' do - get whats_new_path, xhr: true + get whats_new_path(version: 42), xhr: true - expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to eq(highlights.items.to_json) + expect(response.headers['X-Next-Page']).to be_nil end end end |