Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples/requests/api')
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/create_list_shared_examples.rb51
-rw-r--r--spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb62
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb490
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb190
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/requests/api/packages_tags_shared_examples.rb185
-rw-r--r--spec/support/shared_examples/requests/api/read_user_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb3
12 files changed, 755 insertions, 386 deletions
diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
index 83ba72c12aa..acaa0d8c2bc 100644
--- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_context 'Debian repository shared context' do |object_type|
+ include_context 'workhorse headers'
+
before do
stub_feature_flags(debian_packages: true)
end
@@ -37,16 +39,15 @@ RSpec.shared_context 'Debian repository shared context' do |object_type|
let(:params) { workhorse_params }
let(:auth_headers) { {} }
- let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
- let(:workhorse_headers) do
+ let(:wh_headers) do
if method == :put
- { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token }
+ workhorse_headers
else
{}
end
end
- let(:headers) { auth_headers.merge(workhorse_headers) }
+ let(:headers) { auth_headers.merge(wh_headers) }
let(:send_rewritten_field) { true }
@@ -201,7 +202,7 @@ RSpec.shared_examples 'rejects Debian access with unknown project id' do
let(:project) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'Debian project repository GET request', :anonymous, true, :not_found, nil
+ it_behaves_like 'Debian project repository GET request', :anonymous, true, :unauthorized, nil
end
context 'as authenticated user' do
@@ -228,13 +229,13 @@ RSpec.shared_examples 'Debian project repository GET endpoint' do |success_statu
'PUBLIC' | :anonymous | false | true | success_status | success_body
'PRIVATE' | :developer | true | true | success_status | success_body
'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :not_found | nil
- 'PRIVATE' | :guest | true | false | :not_found | nil
+ 'PRIVATE' | :developer | true | false | :unauthorized | nil
+ 'PRIVATE' | :guest | true | false | :unauthorized | nil
'PRIVATE' | :developer | false | true | :not_found | nil
'PRIVATE' | :guest | false | true | :not_found | nil
- 'PRIVATE' | :developer | false | false | :not_found | nil
- 'PRIVATE' | :guest | false | false | :not_found | nil
- 'PRIVATE' | :anonymous | false | true | :not_found | nil
+ 'PRIVATE' | :developer | false | false | :unauthorized | nil
+ 'PRIVATE' | :guest | false | false | :unauthorized | nil
+ 'PRIVATE' | :anonymous | false | true | :unauthorized | nil
end
with_them do
@@ -263,13 +264,13 @@ RSpec.shared_examples 'Debian project repository PUT endpoint' do |success_statu
'PUBLIC' | :anonymous | false | true | :unauthorized | nil
'PRIVATE' | :developer | true | true | success_status | nil
'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :not_found | nil
- 'PRIVATE' | :guest | true | false | :not_found | nil
+ 'PRIVATE' | :developer | true | false | :unauthorized | nil
+ 'PRIVATE' | :guest | true | false | :unauthorized | nil
'PRIVATE' | :developer | false | true | :not_found | nil
'PRIVATE' | :guest | false | true | :not_found | nil
- 'PRIVATE' | :developer | false | false | :not_found | nil
- 'PRIVATE' | :guest | false | false | :not_found | nil
- 'PRIVATE' | :anonymous | false | true | :not_found | nil
+ 'PRIVATE' | :developer | false | false | :unauthorized | nil
+ 'PRIVATE' | :guest | false | false | :unauthorized | nil
+ 'PRIVATE' | :anonymous | false | true | :unauthorized | nil
end
with_them do
@@ -321,7 +322,7 @@ RSpec.shared_examples 'rejects Debian access with unknown group id' do
let(:group) { double(id: non_existing_record_id) }
context 'as anonymous' do
- it_behaves_like 'Debian group repository GET request', :anonymous, true, :not_found, nil
+ it_behaves_like 'Debian group repository GET request', :anonymous, true, :unauthorized, nil
end
context 'as authenticated user' do
@@ -348,13 +349,13 @@ RSpec.shared_examples 'Debian group repository GET endpoint' do |success_status,
'PUBLIC' | :anonymous | false | true | success_status | success_body
'PRIVATE' | :developer | true | true | success_status | success_body
'PRIVATE' | :guest | true | true | :forbidden | nil
- 'PRIVATE' | :developer | true | false | :not_found | nil
- 'PRIVATE' | :guest | true | false | :not_found | nil
+ 'PRIVATE' | :developer | true | false | :unauthorized | nil
+ 'PRIVATE' | :guest | true | false | :unauthorized | nil
'PRIVATE' | :developer | false | true | :not_found | nil
'PRIVATE' | :guest | false | true | :not_found | nil
- 'PRIVATE' | :developer | false | false | :not_found | nil
- 'PRIVATE' | :guest | false | false | :not_found | nil
- 'PRIVATE' | :anonymous | false | true | :not_found | nil
+ 'PRIVATE' | :developer | false | false | :unauthorized | nil
+ 'PRIVATE' | :guest | false | false | :unauthorized | nil
+ 'PRIVATE' | :anonymous | false | true | :unauthorized | nil
end
with_them do
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/create_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/create_list_shared_examples.rb
new file mode 100644
index 00000000000..fe2cdbe3182
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/create_list_shared_examples.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'board lists create request' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:dev_label) do
+ create(:group_label, title: 'Development', color: '#FFAABB', group: group)
+ end
+
+ let(:mutation) { graphql_mutation(mutation_name, input) }
+ let(:mutation_response) { graphql_mutation_response(mutation_name) }
+
+ context 'the user is not allowed to read board lists' do
+ let(:input) { { board_id: board.to_global_id.to_s, backlog: true } }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user has permissions to admin board lists' do
+ before do
+ group.add_reporter(current_user)
+ end
+
+ describe 'backlog list' do
+ let(:input) { { board_id: board.to_global_id.to_s, backlog: true } }
+
+ it 'creates the list' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['list'])
+ .to include('position' => nil, 'listType' => 'backlog')
+ end
+ end
+
+ describe 'label list' do
+ let(:input) { { board_id: board.to_global_id.to_s, label_id: dev_label.to_global_id.to_s } }
+
+ it 'creates the list' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['list'])
+ .to include('position' => 0, 'listType' => 'label', 'label' => include('title' => 'Development'))
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
new file mode 100644
index 00000000000..9cf5bc04f65
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/noteable_shared_examples.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+# Requires `query(fields)`, `path_to_noteable`, `project`, and `noteable` bindings
+RSpec.shared_examples 'a noteable graphql type we can query' do
+ let(:note_factory) { :note }
+ let(:discussion_factory) { :discussion_note }
+
+ describe '.discussions' do
+ let(:fields) do
+ "discussions { nodes { #{all_graphql_fields_for('Discussion')} } }"
+ end
+
+ def expected
+ noteable.discussions.map do |discussion|
+ include(
+ 'id' => global_id_of(discussion),
+ 'replyId' => global_id_of(discussion, id: discussion.reply_id),
+ 'createdAt' => discussion.created_at.iso8601,
+ 'notes' => include(
+ 'nodes' => have_attributes(size: discussion.notes.size)
+ )
+ )
+ end
+ end
+
+ it 'can fetch discussions' do
+ create(discussion_factory, project: project, noteable: noteable)
+
+ post_graphql(query(fields), current_user: current_user)
+
+ expect(graphql_data_at(*path_to_noteable, :discussions, :nodes))
+ .to match_array(expected)
+ end
+ end
+
+ describe '.notes' do
+ let(:fields) do
+ "notes { nodes { #{all_graphql_fields_for('Note', max_depth: 2)} } }"
+ end
+
+ def expected
+ noteable.notes.map do |note|
+ include(
+ 'id' => global_id_of(note),
+ 'project' => include('id' => global_id_of(project)),
+ 'author' => include('id' => global_id_of(note.author)),
+ 'createdAt' => note.created_at.iso8601,
+ 'body' => eq(note.note)
+ )
+ end
+ end
+
+ it 'can fetch notes' do
+ create(note_factory, project: project, noteable: noteable)
+
+ post_graphql(query(fields), current_user: current_user)
+
+ expect(graphql_data_at(*path_to_noteable, :notes, :nodes))
+ .to match_array(expected)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index 7066f803f9d..40799688144 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -127,6 +127,12 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
+ let(:params) { { body: 'hi!' } }
+
+ subject do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ end
+
it "creates a new note" do
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: { body: 'hi!' }
@@ -274,6 +280,29 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when request exceeds the rate limit' do
+ before do
+ stub_application_setting(notes_create_limit: 1)
+ allow(::Gitlab::ApplicationRateLimiter).to receive(:increment).and_return(2)
+ end
+
+ it 'prevents user from creating more notes' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
+ end
+
+ it 'allows user in allow-list to create notes' do
+ stub_application_setting(notes_create_limit_allowlist: ["#{user.username}"])
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['author']['username']).to eq(user.username)
+ end
+ end
end
describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index d3ad7aa0595..be051dcbb7b 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -1,270 +1,430 @@
# frozen_string_literal: true
-RSpec.shared_examples 'handling get metadata requests' do
+RSpec.shared_examples 'handling get metadata requests' do |scope: :project|
+ using RSpec::Parameterized::TableSyntax
+
let_it_be(:package_dependency_link1) { create(:packages_dependency_link, package: package, dependency_type: :dependencies) }
let_it_be(:package_dependency_link2) { create(:packages_dependency_link, package: package, dependency_type: :devDependencies) }
let_it_be(:package_dependency_link3) { create(:packages_dependency_link, package: package, dependency_type: :bundleDependencies) }
let_it_be(:package_dependency_link4) { create(:packages_dependency_link, package: package, dependency_type: :peerDependencies) }
- let(:params) { {} }
let(:headers) { {} }
- subject { get(url, params: params, headers: headers) }
+ subject { get(url, headers: headers) }
- shared_examples 'returning the npm package info' do
- it 'returns the package info' do
+ shared_examples 'accept metadata request' do |status:|
+ it 'accepts the metadata request' do
subject
- expect_a_valid_package_response
+ expect(response).to have_gitlab_http_status(status)
+ expect(response.media_type).to eq('application/json')
+ expect(response).to match_response_schema('public_api/v4/packages/npm_package')
+ expect(json_response['name']).to eq(package.name)
+ expect(json_response['versions'][package.version]).to match_schema('public_api/v4/packages/npm_package_version')
+ ::Packages::Npm::PackagePresenter::NPM_VALID_DEPENDENCY_TYPES.each do |dependency_type|
+ expect(json_response.dig('versions', package.version, dependency_type.to_s)).to be_any
+ end
+ expect(json_response['dist-tags']).to match_schema('public_api/v4/packages/npm_package_tags')
end
end
- shared_examples 'a package that requires auth' do
- it 'denies request without oauth token' do
+ shared_examples 'reject metadata request' do |status:|
+ it 'rejects the metadata request' do
subject
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(status)
end
+ end
- context 'with oauth token' do
- let(:params) { { access_token: token.token } }
-
- it 'returns the package info with oauth token' do
- subject
+ shared_examples 'redirect metadata request' do |status:|
+ it 'redirects metadata request' do
+ subject
- expect_a_valid_package_response
- end
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response.headers['Location']).to eq("https://registry.npmjs.org/#{package_name}")
end
+ end
- context 'with job token' do
- let(:params) { { job_token: job.token } }
-
- it 'returns the package info with running job token' do
- subject
+ where(:auth, :package_name_type, :request_forward, :visibility, :user_role, :expected_result, :expected_status) do
+ nil | :scoped_naming_convention | true | 'PUBLIC' | nil | :accept | :ok
+ nil | :scoped_naming_convention | false | 'PUBLIC' | nil | :accept | :ok
+ nil | :non_existing | true | 'PUBLIC' | nil | :redirect | :redirected
+ nil | :non_existing | false | 'PUBLIC' | nil | :reject | :not_found
+ nil | :scoped_naming_convention | true | 'PRIVATE' | nil | :reject | :not_found
+ nil | :scoped_naming_convention | false | 'PRIVATE' | nil | :reject | :not_found
+ nil | :non_existing | true | 'PRIVATE' | nil | :redirect | :redirected
+ nil | :non_existing | false | 'PRIVATE' | nil | :reject | :not_found
+ nil | :scoped_naming_convention | true | 'INTERNAL' | nil | :reject | :not_found
+ nil | :scoped_naming_convention | false | 'INTERNAL' | nil | :reject | :not_found
+ nil | :non_existing | true | 'INTERNAL' | nil | :redirect | :redirected
+ nil | :non_existing | false | 'INTERNAL' | nil | :reject | :not_found
+
+ :oauth | :scoped_naming_convention | true | 'PUBLIC' | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | true | 'PUBLIC' | :reporter | :accept | :ok
+ :oauth | :scoped_naming_convention | false | 'PUBLIC' | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | false | 'PUBLIC' | :reporter | :accept | :ok
+ :oauth | :non_existing | true | 'PUBLIC' | :guest | :redirect | :redirected
+ :oauth | :non_existing | true | 'PUBLIC' | :reporter | :redirect | :redirected
+ :oauth | :non_existing | false | 'PUBLIC' | :guest | :reject | :not_found
+ :oauth | :non_existing | false | 'PUBLIC' | :reporter | :reject | :not_found
+ :oauth | :scoped_naming_convention | true | 'PRIVATE' | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | true | 'PRIVATE' | :reporter | :accept | :ok
+ :oauth | :scoped_naming_convention | false | 'PRIVATE' | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | false | 'PRIVATE' | :reporter | :accept | :ok
+ :oauth | :non_existing | true | 'PRIVATE' | :guest | :redirect | :redirected
+ :oauth | :non_existing | true | 'PRIVATE' | :reporter | :redirect | :redirected
+ :oauth | :non_existing | false | 'PRIVATE' | :guest | :reject | :forbidden
+ :oauth | :non_existing | false | 'PRIVATE' | :reporter | :reject | :not_found
+ :oauth | :scoped_naming_convention | true | 'INTERNAL' | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | true | 'INTERNAL' | :reporter | :accept | :ok
+ :oauth | :scoped_naming_convention | false | 'INTERNAL' | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | false | 'INTERNAL' | :reporter | :accept | :ok
+ :oauth | :non_existing | true | 'INTERNAL' | :guest | :redirect | :redirected
+ :oauth | :non_existing | true | 'INTERNAL' | :reporter | :redirect | :redirected
+ :oauth | :non_existing | false | 'INTERNAL' | :guest | :reject | :not_found
+ :oauth | :non_existing | false | 'INTERNAL' | :reporter | :reject | :not_found
+
+ :personal_access_token | :scoped_naming_convention | true | 'PUBLIC' | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | true | 'PUBLIC' | :reporter | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | false | 'PUBLIC' | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | false | 'PUBLIC' | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | true | 'PUBLIC' | :guest | :redirect | :redirected
+ :personal_access_token | :non_existing | true | 'PUBLIC' | :reporter | :redirect | :redirected
+ :personal_access_token | :non_existing | false | 'PUBLIC' | :guest | :reject | :not_found
+ :personal_access_token | :non_existing | false | 'PUBLIC' | :reporter | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | true | 'PRIVATE' | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | true | 'PRIVATE' | :reporter | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | false | 'PRIVATE' | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | false | 'PRIVATE' | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | true | 'PRIVATE' | :guest | :redirect | :redirected
+ :personal_access_token | :non_existing | true | 'PRIVATE' | :reporter | :redirect | :redirected
+ :personal_access_token | :non_existing | false | 'PRIVATE' | :guest | :reject | :forbidden
+ :personal_access_token | :non_existing | false | 'PRIVATE' | :reporter | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | true | 'INTERNAL' | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | true | 'INTERNAL' | :reporter | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | false | 'INTERNAL' | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | false | 'INTERNAL' | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | true | 'INTERNAL' | :guest | :redirect | :redirected
+ :personal_access_token | :non_existing | true | 'INTERNAL' | :reporter | :redirect | :redirected
+ :personal_access_token | :non_existing | false | 'INTERNAL' | :guest | :reject | :not_found
+ :personal_access_token | :non_existing | false | 'INTERNAL' | :reporter | :reject | :not_found
+
+ :job_token | :scoped_naming_convention | true | 'PUBLIC' | :developer | :accept | :ok
+ :job_token | :scoped_naming_convention | false | 'PUBLIC' | :developer | :accept | :ok
+ :job_token | :non_existing | true | 'PUBLIC' | :developer | :redirect | :redirected
+ :job_token | :non_existing | false | 'PUBLIC' | :developer | :reject | :not_found
+ :job_token | :scoped_naming_convention | true | 'PRIVATE' | :developer | :accept | :ok
+ :job_token | :scoped_naming_convention | false | 'PRIVATE' | :developer | :accept | :ok
+ :job_token | :non_existing | true | 'PRIVATE' | :developer | :redirect | :redirected
+ :job_token | :non_existing | false | 'PRIVATE' | :developer | :reject | :not_found
+ :job_token | :scoped_naming_convention | true | 'INTERNAL' | :developer | :accept | :ok
+ :job_token | :scoped_naming_convention | false | 'INTERNAL' | :developer | :accept | :ok
+ :job_token | :non_existing | true | 'INTERNAL' | :developer | :redirect | :redirected
+ :job_token | :non_existing | false | 'INTERNAL' | :developer | :reject | :not_found
+
+ :deploy_token | :scoped_naming_convention | true | 'PUBLIC' | nil | :accept | :ok
+ :deploy_token | :scoped_naming_convention | false | 'PUBLIC' | nil | :accept | :ok
+ :deploy_token | :non_existing | true | 'PUBLIC' | nil | :redirect | :redirected
+ :deploy_token | :non_existing | false | 'PUBLIC' | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | true | 'PRIVATE' | nil | :accept | :ok
+ :deploy_token | :scoped_naming_convention | false | 'PRIVATE' | nil | :accept | :ok
+ :deploy_token | :non_existing | true | 'PRIVATE' | nil | :redirect | :redirected
+ :deploy_token | :non_existing | false | 'PRIVATE' | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | true | 'INTERNAL' | nil | :accept | :ok
+ :deploy_token | :scoped_naming_convention | false | 'INTERNAL' | nil | :accept | :ok
+ :deploy_token | :non_existing | true | 'INTERNAL' | nil | :redirect | :redirected
+ :deploy_token | :non_existing | false | 'INTERNAL' | nil | :reject | :not_found
+ end
- expect_a_valid_package_response
+ with_them do
+ include_context 'set package name from package name type'
+
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
end
+ end
- it 'denies request without running job token' do
- job.update!(status: :success)
+ before do
+ project.send("add_#{user_role}", user) if user_role
+ project.update!(visibility: Gitlab::VisibilityLevel.const_get(visibility, false))
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
+ stub_application_setting(npm_package_requests_forwarding: request_forward)
+ end
- subject
+ example_name = "#{params[:expected_result]} metadata request"
+ status = params[:expected_status]
- expect(response).to have_gitlab_http_status(:unauthorized)
+ if scope == :instance && params[:package_name_type] != :scoped_naming_convention
+ if params[:request_forward]
+ example_name = 'redirect metadata request'
+ status = :redirected
+ else
+ example_name = 'reject metadata request'
+ status = :not_found
end
end
- context 'with deploy token' do
- let(:headers) { build_token_auth_header(deploy_token.token) }
+ it_behaves_like example_name, status: status
+ end
- it 'returns the package info with deploy token' do
- subject
+ context 'with a developer' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
- expect_a_valid_package_response
- end
+ before do
+ project.add_developer(user)
end
- end
-
- context 'a public project' do
- it_behaves_like 'returning the npm package info'
context 'project path with a dot' do
before do
project.update!(path: 'foo.bar')
end
- it_behaves_like 'returning the npm package info'
+ it_behaves_like 'accept metadata request', status: :ok
end
- context 'with request forward disabled' do
+ context 'with a job token' do
+ let(:headers) { build_token_auth_header(job.token) }
+
before do
- stub_application_setting(npm_package_requests_forwarding: false)
+ job.update!(status: :success)
end
- it_behaves_like 'returning the npm package info'
+ it_behaves_like 'reject metadata request', status: :unauthorized
+ end
+ end
+end
+
+RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
+ using RSpec::Parameterized::TableSyntax
+ include_context 'set package name from package name type'
- context 'with unknown package' do
- let(:package_name) { 'unknown' }
+ let_it_be(:package_tag1) { create(:packages_tag, package: package) }
+ let_it_be(:package_tag2) { create(:packages_tag, package: package) }
- it 'returns the proper response' do
- subject
+ let(:headers) { {} }
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
+ subject { get(url, headers: headers) }
+
+ shared_examples 'reject package tags request' do |status:|
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
end
- context 'with request forward enabled' do
- before do
- stub_application_setting(npm_package_requests_forwarding: true)
- end
+ it_behaves_like 'returning response status', status
+ end
- it_behaves_like 'returning the npm package info'
+ shared_examples 'handling different package names, visibilities and user roles' do
+ where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
+ :scoped_naming_convention | 'PUBLIC' | :anonymous | :accept | :ok
+ :scoped_naming_convention | 'PUBLIC' | :guest | :accept | :ok
+ :scoped_naming_convention | 'PUBLIC' | :reporter | :accept | :ok
+ :non_existing | 'PUBLIC' | :anonymous | :reject | :not_found
+ :non_existing | 'PUBLIC' | :guest | :reject | :not_found
+ :non_existing | 'PUBLIC' | :reporter | :reject | :not_found
+
+ :scoped_naming_convention | 'PRIVATE' | :anonymous | :reject | :not_found
+ :scoped_naming_convention | 'PRIVATE' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'PRIVATE' | :reporter | :accept | :ok
+ :non_existing | 'PRIVATE' | :anonymous | :reject | :not_found
+ :non_existing | 'PRIVATE' | :guest | :reject | :forbidden
+ :non_existing | 'PRIVATE' | :reporter | :reject | :not_found
+
+ :scoped_naming_convention | 'INTERNAL' | :anonymous | :reject | :not_found
+ :scoped_naming_convention | 'INTERNAL' | :guest | :accept | :ok
+ :scoped_naming_convention | 'INTERNAL' | :reporter | :accept | :ok
+ :non_existing | 'INTERNAL' | :anonymous | :reject | :not_found
+ :non_existing | 'INTERNAL' | :guest | :reject | :not_found
+ :non_existing | 'INTERNAL' | :reporter | :reject | :not_found
+ end
+
+ with_them do
+ let(:anonymous) { user_role == :anonymous }
- context 'with unknown package' do
- let(:package_name) { 'unknown' }
+ subject { get(url, headers: anonymous ? {} : headers) }
- it 'returns a redirect' do
- subject
+ before do
+ project.send("add_#{user_role}", user) unless anonymous
+ project.update!(visibility: Gitlab::VisibilityLevel.const_get(visibility, false))
+ end
- expect(response).to have_gitlab_http_status(:found)
- expect(response.headers['Location']).to eq('https://registry.npmjs.org/unknown')
- end
+ example_name = "#{params[:expected_result]} package tags request"
+ status = params[:expected_status]
- it_behaves_like 'a gitlab tracking event', described_class.name, 'npm_request_forward'
+ if scope == :instance && params[:package_name_type] != :scoped_naming_convention
+ example_name = 'reject package tags request'
+ status = :not_found
end
+
+ it_behaves_like example_name, status: status
end
end
- context 'internal project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- end
+ context 'with oauth token' do
+ let(:headers) { build_token_auth_header(token.token) }
- it_behaves_like 'a package that requires auth'
+ it_behaves_like 'handling different package names, visibilities and user roles'
end
- context 'private project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ context 'with personal access token' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
- it_behaves_like 'a package that requires auth'
+ it_behaves_like 'handling different package names, visibilities and user roles'
+ end
+end
- context 'with guest' do
- let(:params) { { access_token: token.token } }
+RSpec.shared_examples 'handling create dist tag requests' do |scope: :project|
+ using RSpec::Parameterized::TableSyntax
+ include_context 'set package name from package name type'
- it 'denies request when not enough permissions' do
- project.add_guest(user)
+ let_it_be(:tag_name) { 'test' }
- subject
+ let(:params) { {} }
+ let(:version) { package.version }
+ let(:env) { { 'api.request.body': version } }
+ let(:headers) { {} }
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ shared_examples 'reject create package tag request' do |status:|
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
end
+
+ it_behaves_like 'returning response status', status
end
- def expect_a_valid_package_response
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/json')
- expect(response).to match_response_schema('public_api/v4/packages/npm_package')
- expect(json_response['name']).to eq(package.name)
- expect(json_response['versions'][package.version]).to match_schema('public_api/v4/packages/npm_package_version')
- ::Packages::Npm::PackagePresenter::NPM_VALID_DEPENDENCY_TYPES.each do |dependency_type|
- expect(json_response.dig('versions', package.version, dependency_type.to_s)).to be_any
+ shared_examples 'handling different package names, visibilities and user roles' do
+ where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
+ :scoped_naming_convention | 'PUBLIC' | :anonymous | :reject | :forbidden
+ :scoped_naming_convention | 'PUBLIC' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'PUBLIC' | :developer | :accept | :ok
+ :non_existing | 'PUBLIC' | :anonymous | :reject | :forbidden
+ :non_existing | 'PUBLIC' | :guest | :reject | :forbidden
+ :non_existing | 'PUBLIC' | :developer | :reject | :not_found
+
+ :scoped_naming_convention | 'PRIVATE' | :anonymous | :reject | :not_found
+ :scoped_naming_convention | 'PRIVATE' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'PRIVATE' | :developer | :accept | :ok
+ :non_existing | 'PRIVATE' | :anonymous | :reject | :not_found
+ :non_existing | 'PRIVATE' | :guest | :reject | :forbidden
+ :non_existing | 'PRIVATE' | :developer | :reject | :not_found
+
+ :scoped_naming_convention | 'INTERNAL' | :anonymous | :reject | :forbidden
+ :scoped_naming_convention | 'INTERNAL' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'INTERNAL' | :developer | :accept | :ok
+ :non_existing | 'INTERNAL' | :anonymous | :reject | :forbidden
+ :non_existing | 'INTERNAL' | :guest | :reject | :forbidden
+ :non_existing | 'INTERNAL' | :developer | :reject | :not_found
end
- expect(json_response['dist-tags']).to match_schema('public_api/v4/packages/npm_package_tags')
- end
-end
-RSpec.shared_examples 'handling get dist tags requests' do
- let_it_be(:package_tag1) { create(:packages_tag, package: package) }
- let_it_be(:package_tag2) { create(:packages_tag, package: package) }
+ with_them do
+ let(:anonymous) { user_role == :anonymous }
- let(:params) { {} }
+ subject { put(url, env: env, headers: headers) }
- subject { get(url, params: params) }
+ before do
+ project.send("add_#{user_role}", user) unless anonymous
+ project.update!(visibility: Gitlab::VisibilityLevel.const_get(visibility, false))
+ end
- context 'with public project' do
- context 'with authenticated user' do
- let(:params) { { private_token: personal_access_token.token } }
+ example_name = "#{params[:expected_result]} create package tag request"
+ status = params[:expected_status]
- it_behaves_like 'returns package tags', :maintainer
- it_behaves_like 'returns package tags', :developer
- it_behaves_like 'returns package tags', :reporter
- it_behaves_like 'returns package tags', :guest
- end
+ if scope == :instance && params[:package_name_type] != :scoped_naming_convention
+ example_name = 'reject create package tag request'
+ status = :not_found
+ end
- context 'with unauthenticated user' do
- it_behaves_like 'returns package tags', :no_type
+ it_behaves_like example_name, status: status
end
end
- context 'with private project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ context 'with oauth token' do
+ let(:headers) { build_token_auth_header(token.token) }
- context 'with authenticated user' do
- let(:params) { { private_token: personal_access_token.token } }
+ it_behaves_like 'handling different package names, visibilities and user roles'
+ end
- it_behaves_like 'returns package tags', :maintainer
- it_behaves_like 'returns package tags', :developer
- it_behaves_like 'returns package tags', :reporter
- it_behaves_like 'rejects package tags access', :guest, :forbidden
- end
+ context 'with personal access token' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
- context 'with unauthenticated user' do
- it_behaves_like 'rejects package tags access', :no_type, :not_found
- end
+ it_behaves_like 'handling different package names, visibilities and user roles'
end
end
-RSpec.shared_examples 'handling create dist tag requests' do
- let_it_be(:tag_name) { 'test' }
+RSpec.shared_examples 'handling delete dist tag requests' do |scope: :project|
+ using RSpec::Parameterized::TableSyntax
+ include_context 'set package name from package name type'
- let(:params) { {} }
- let(:env) { {} }
- let(:version) { package.version }
-
- subject { put(url, env: env, params: params) }
+ let_it_be(:package_tag) { create(:packages_tag, package: package) }
- context 'with public project' do
- context 'with authenticated user' do
- let(:params) { { private_token: personal_access_token.token } }
- let(:env) { { 'api.request.body': version } }
+ let(:tag_name) { package_tag.name }
+ let(:headers) { {} }
- it_behaves_like 'create package tag', :maintainer
- it_behaves_like 'create package tag', :developer
- it_behaves_like 'rejects package tags access', :reporter, :forbidden
- it_behaves_like 'rejects package tags access', :guest, :forbidden
+ shared_examples 'reject delete package tag request' do |status:|
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
end
- context 'with unauthenticated user' do
- it_behaves_like 'rejects package tags access', :no_type, :unauthorized
- end
+ it_behaves_like 'returning response status', status
end
-end
-RSpec.shared_examples 'handling delete dist tag requests' do
- let_it_be(:package_tag) { create(:packages_tag, package: package) }
+ shared_examples 'handling different package names, visibilities and user roles' do
+ where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
+ :scoped_naming_convention | 'PUBLIC' | :anonymous | :reject | :forbidden
+ :scoped_naming_convention | 'PUBLIC' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'PUBLIC' | :maintainer | :accept | :ok
+ :non_existing | 'PUBLIC' | :anonymous | :reject | :forbidden
+ :non_existing | 'PUBLIC' | :guest | :reject | :forbidden
+ :non_existing | 'PUBLIC' | :maintainer | :reject | :not_found
+
+ :scoped_naming_convention | 'PRIVATE' | :anonymous | :reject | :not_found
+ :scoped_naming_convention | 'PRIVATE' | :guest | :reject | :forbidden
+ :scoped_naming_convention | 'PRIVATE' | :maintainer | :accept | :ok
+ :non_existing | 'INTERNAL' | :anonymous | :reject | :forbidden
+ :non_existing | 'INTERNAL' | :guest | :reject | :forbidden
+ :non_existing | 'INTERNAL' | :maintainer | :reject | :not_found
+ end
- let(:params) { {} }
- let(:tag_name) { package_tag.name }
+ with_them do
+ let(:anonymous) { user_role == :anonymous }
+
+ subject { delete(url, headers: headers) }
- subject { delete(url, params: params) }
+ before do
+ project.send("add_#{user_role}", user) unless anonymous
+ project.update!(visibility: Gitlab::VisibilityLevel.const_get(visibility, false))
+ end
- context 'with public project' do
- context 'with authenticated user' do
- let(:params) { { private_token: personal_access_token.token } }
+ example_name = "#{params[:expected_result]} delete package tag request"
+ status = params[:expected_status]
- it_behaves_like 'delete package tag', :maintainer
- it_behaves_like 'rejects package tags access', :developer, :forbidden
- it_behaves_like 'rejects package tags access', :reporter, :forbidden
- it_behaves_like 'rejects package tags access', :guest, :forbidden
- end
+ if scope == :instance && params[:package_name_type] != :scoped_naming_convention
+ example_name = 'reject delete package tag request'
+ status = :not_found
+ end
- context 'with unauthenticated user' do
- it_behaves_like 'rejects package tags access', :no_type, :unauthorized
+ it_behaves_like example_name, status: status
end
end
- context 'with private project' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
+ context 'with oauth token' do
+ let(:headers) { build_token_auth_header(token.token) }
- context 'with authenticated user' do
- let(:params) { { private_token: personal_access_token.token } }
+ it_behaves_like 'handling different package names, visibilities and user roles'
+ end
- it_behaves_like 'delete package tag', :maintainer
- it_behaves_like 'rejects package tags access', :developer, :forbidden
- it_behaves_like 'rejects package tags access', :reporter, :forbidden
- it_behaves_like 'rejects package tags access', :guest, :forbidden
- end
+ context 'with personal access token' do
+ let(:headers) { build_token_auth_header(personal_access_token.token) }
- context 'with unauthenticated user' do
- it_behaves_like 'rejects package tags access', :no_type, :unauthorized
- end
+ it_behaves_like 'handling different package names, visibilities and user roles'
end
end
diff --git a/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb
new file mode 100644
index 00000000000..e6b3dc74b74
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'rejects package tags access' do |status:|
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
+ end
+
+ it_behaves_like 'returning response status', status
+end
+
+RSpec.shared_examples 'accept package tags request' do |status:|
+ using RSpec::Parameterized::TableSyntax
+
+ before do
+ stub_application_setting(npm_package_requests_forwarding: false)
+ end
+
+ context 'with valid package name' do
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
+ end
+
+ it_behaves_like 'returning response status', status
+
+ it 'returns a valid json response' do
+ subject
+
+ expect(response.media_type).to eq('application/json')
+ expect(json_response).to be_a(Hash)
+ end
+
+ it 'returns two package tags' do
+ subject
+
+ expect(json_response).to match_schema('public_api/v4/packages/npm_package_tags')
+ expect(json_response.length).to eq(3) # two tags + latest (auto added)
+ expect(json_response[package_tag1.name]).to eq(package.version)
+ expect(json_response[package_tag2.name]).to eq(package.version)
+ expect(json_response['latest']).to eq(package.version)
+ end
+ end
+
+ context 'with invalid package name' do
+ where(:package_name, :status) do
+ '%20' | :bad_request
+ nil | :not_found
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+end
+
+RSpec.shared_examples 'accept create package tag request' do |user_type|
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid package name' do
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
+ end
+
+ it_behaves_like 'returning response status', :no_content
+
+ it 'creates the package tag' do
+ expect { subject }.to change { Packages::Tag.count }.by(1)
+
+ last_tag = Packages::Tag.last
+ expect(last_tag.name).to eq(tag_name)
+ expect(last_tag.package).to eq(package)
+ end
+
+ it 'returns a valid response' do
+ subject
+
+ expect(response.body).to be_empty
+ end
+
+ context 'with already existing tag' do
+ let_it_be(:package2) { create(:npm_package, project: project, name: package.name, version: '5.5.55') }
+ let_it_be(:tag) { create(:packages_tag, package: package2, name: tag_name) }
+
+ it_behaves_like 'returning response status', :no_content
+
+ it 'reuses existing tag' do
+ expect(package.tags).to be_empty
+ expect(package2.tags).to eq([tag])
+ expect { subject }.to not_change { Packages::Tag.count }
+ expect(package.reload.tags).to eq([tag])
+ expect(package2.reload.tags).to be_empty
+ end
+
+ it 'returns a valid response' do
+ subject
+
+ expect(response.body).to be_empty
+ end
+ end
+ end
+
+ context 'with invalid package name' do
+ where(:package_name, :status) do
+ 'unknown' | :not_found
+ '' | :not_found
+ '%20' | :bad_request
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+
+ context 'with invalid tag name' do
+ where(:tag_name, :status) do
+ '' | :not_found
+ '%20' | :bad_request
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+
+ context 'with invalid version' do
+ where(:version, :status) do
+ ' ' | :bad_request
+ '' | :bad_request
+ nil | :bad_request
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+end
+
+RSpec.shared_examples 'accept delete package tag request' do |user_type|
+ using RSpec::Parameterized::TableSyntax
+
+ context 'with valid package name' do
+ before do
+ package.update!(name: package_name) unless package_name == 'non-existing-package'
+ end
+
+ it_behaves_like 'returning response status', :no_content
+
+ it 'returns a valid response' do
+ subject
+
+ expect(response.body).to be_empty
+ end
+
+ it 'destroy the package tag' do
+ expect(package.tags).to eq([package_tag])
+ expect { subject }.to change { Packages::Tag.count }.by(-1)
+ expect(package.reload.tags).to be_empty
+ end
+
+ context 'with tag from other package' do
+ let(:package2) { create(:npm_package, project: project) }
+ let(:package_tag) { create(:packages_tag, package: package2) }
+
+ it_behaves_like 'returning response status', :not_found
+ end
+ end
+
+ context 'with invalid package name' do
+ where(:package_name, :status) do
+ 'unknown' | :not_found
+ '' | :not_found
+ '%20' | :bad_request
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+
+ context 'with invalid tag name' do
+ where(:tag_name, :status) do
+ 'unknown' | :not_found
+ '' | :not_found
+ '%20' | :bad_request
+ end
+
+ with_them do
+ it_behaves_like 'returning response status', params[:status]
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index 8b60857cdaf..617fdecbb5b 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -123,7 +123,7 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
context 'with a request that bypassed gitlab-workhorse' do
let(:headers) do
basic_auth_header(user.username, personal_access_token.token)
- .merge(workhorse_header)
+ .merge(workhorse_headers)
.tap { |h| h.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) }
end
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index 3833604e304..15976eed021 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -24,7 +24,7 @@ end
RSpec.shared_examples 'deploy token for package uploads' do
context 'with deploy token headers' do
- let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@@ -35,7 +35,7 @@ RSpec.shared_examples 'deploy token for package uploads' do
end
context 'invalid token' do
- let(:headers) { basic_auth_header(deploy_token.username, 'bar').merge(workhorse_header) }
+ let(:headers) { basic_auth_header(deploy_token.username, 'bar').merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end
@@ -102,7 +102,7 @@ end
RSpec.shared_examples 'job token for package uploads' do
context 'with job token headers' do
- let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token).merge(workhorse_header) }
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token).merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@@ -114,13 +114,13 @@ RSpec.shared_examples 'job token for package uploads' do
end
context 'invalid token' do
- let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar').merge(workhorse_header) }
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar').merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end
context 'invalid user' do
- let(:headers) { basic_auth_header('foo', job.token).merge(workhorse_header) }
+ let(:headers) { basic_auth_header('foo', job.token).merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end
diff --git a/spec/support/shared_examples/requests/api/packages_tags_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_tags_shared_examples.rb
deleted file mode 100644
index 2c203dc096e..00000000000
--- a/spec/support/shared_examples/requests/api/packages_tags_shared_examples.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'rejects package tags access' do |user_type, status|
- context "for user type #{user_type}" do
- before do
- project.send("add_#{user_type}", user) unless user_type == :no_type
- end
-
- it_behaves_like 'returning response status', status
- end
-end
-
-RSpec.shared_examples 'returns package tags' do |user_type|
- using RSpec::Parameterized::TableSyntax
-
- before do
- stub_application_setting(npm_package_requests_forwarding: false)
- project.send("add_#{user_type}", user) unless user_type == :no_type
- end
-
- it_behaves_like 'returning response status', :success
-
- it 'returns a valid json response' do
- subject
-
- expect(response.media_type).to eq('application/json')
- expect(json_response).to be_a(Hash)
- end
-
- it 'returns two package tags' do
- subject
-
- expect(json_response).to match_schema('public_api/v4/packages/npm_package_tags')
- expect(json_response.length).to eq(3) # two tags + latest (auto added)
- expect(json_response[package_tag1.name]).to eq(package.version)
- expect(json_response[package_tag2.name]).to eq(package.version)
- expect(json_response['latest']).to eq(package.version)
- end
-
- context 'with invalid package name' do
- where(:package_name, :status) do
- '%20' | :bad_request
- nil | :not_found
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
-end
-
-RSpec.shared_examples 'create package tag' do |user_type|
- using RSpec::Parameterized::TableSyntax
-
- before do
- project.send("add_#{user_type}", user) unless user_type == :no_type
- end
-
- it_behaves_like 'returning response status', :no_content
-
- it 'creates the package tag' do
- expect { subject }.to change { Packages::Tag.count }.by(1)
-
- last_tag = Packages::Tag.last
- expect(last_tag.name).to eq(tag_name)
- expect(last_tag.package).to eq(package)
- end
-
- it 'returns a valid response' do
- subject
-
- expect(response.body).to be_empty
- end
-
- context 'with already existing tag' do
- let_it_be(:package2) { create(:npm_package, project: project, name: package.name, version: '5.5.55') }
- let_it_be(:tag) { create(:packages_tag, package: package2, name: tag_name) }
-
- it_behaves_like 'returning response status', :no_content
-
- it 'reuses existing tag' do
- expect(package.tags).to be_empty
- expect(package2.tags).to eq([tag])
- expect { subject }.to not_change { Packages::Tag.count }
- expect(package.reload.tags).to eq([tag])
- expect(package2.reload.tags).to be_empty
- end
-
- it 'returns a valid response' do
- subject
-
- expect(response.body).to be_empty
- end
- end
-
- context 'with invalid package name' do
- where(:package_name, :status) do
- 'unknown' | :not_found
- '' | :not_found
- '%20' | :bad_request
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
-
- context 'with invalid tag name' do
- where(:tag_name, :status) do
- '' | :not_found
- '%20' | :bad_request
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
-
- context 'with invalid version' do
- where(:version, :status) do
- ' ' | :bad_request
- '' | :bad_request
- nil | :bad_request
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
-end
-
-RSpec.shared_examples 'delete package tag' do |user_type|
- using RSpec::Parameterized::TableSyntax
-
- before do
- project.send("add_#{user_type}", user) unless user_type == :no_type
- end
-
- context "for #{user_type} user" do
- it_behaves_like 'returning response status', :no_content
-
- it 'returns a valid response' do
- subject
-
- expect(response.body).to be_empty
- end
-
- it 'destroy the package tag' do
- expect(package.tags).to eq([package_tag])
- expect { subject }.to change { Packages::Tag.count }.by(-1)
- expect(package.reload.tags).to be_empty
- end
-
- context 'with tag from other package' do
- let(:package2) { create(:npm_package, project: project) }
- let(:package_tag) { create(:packages_tag, package: package2) }
-
- it_behaves_like 'returning response status', :not_found
- end
-
- context 'with invalid package name' do
- where(:package_name, :status) do
- 'unknown' | :not_found
- '' | :not_found
- '%20' | :bad_request
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
-
- context 'with invalid tag name' do
- where(:tag_name, :status) do
- 'unknown' | :not_found
- '' | :not_found
- '%20' | :bad_request
- end
-
- with_them do
- it_behaves_like 'returning response status', params[:status]
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/requests/api/read_user_shared_examples.rb b/spec/support/shared_examples/requests/api/read_user_shared_examples.rb
index 59cd0ab67b4..b9fd997bd2c 100644
--- a/spec/support/shared_examples/requests/api/read_user_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/read_user_shared_examples.rb
@@ -7,21 +7,33 @@ RSpec.shared_examples 'allows the "read_user" scope' do |api_version|
context 'when the requesting token has the "api" scope' do
let(:token) { create(:personal_access_token, scopes: ['api'], user: user) }
- it 'returns a "200" response' do
+ it 'returns a "200" response on get request' do
get api_call.call(path, user, personal_access_token: token, version: version)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'returns a "200" response on head request' do
+ head api_call.call(path, user, personal_access_token: token, version: version)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'when the requesting token has the "read_user" scope' do
let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) }
- it 'returns a "200" response' do
+ it 'returns a "200" response on get request' do
get api_call.call(path, user, personal_access_token: token, version: version)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'returns a "200" response on head request' do
+ head api_call.call(path, user, personal_access_token: token, version: version)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'when the requesting token does not have any required scope' do
@@ -45,21 +57,33 @@ RSpec.shared_examples 'allows the "read_user" scope' do |api_version|
context 'when the requesting token has the "api" scope' do
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "api" }
- it 'returns a "200" response' do
+ it 'returns a "200" response on get request' do
get api_call.call(path, user, oauth_access_token: token)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'returns a "200" response on head request' do
+ head api_call.call(path, user, oauth_access_token: token)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'when the requesting token has the "read_user" scope' do
let!(:token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "read_user" }
- it 'returns a "200" response' do
+ it 'returns a "200" response on get request' do
get api_call.call(path, user, oauth_access_token: token)
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'returns a "200" response on head request' do
+ head api_call.call(path, user, oauth_access_token: token)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'when the requesting token does not have any required scope' do
diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
index b2970fd265d..3ca2b9fa6de 100644
--- a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
@@ -85,14 +85,37 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
end
describe "GET /#{container_type}/:id/repository_storage_moves" do
- it_behaves_like 'get container repository storage move list' do
- let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves" }
+ let(:container_id) { container.id }
+ let(:url) { "/#{container_type}/#{container_id}/repository_storage_moves" }
+
+ it_behaves_like 'get container repository storage move list'
+
+ context 'non-existent container' do
+ let(:container_id) { non_existing_record_id }
+
+ it 'returns not found' do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
end
describe "GET /#{container_type}/:id/repository_storage_moves/:repository_storage_move_id" do
- it_behaves_like 'get single container repository storage move' do
- let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves/#{repository_storage_move_id}" }
+ let(:container_id) { container.id }
+ let(:url) { "/#{container_type}/#{container_id}/repository_storage_moves/#{repository_storage_move_id}" }
+
+ it_behaves_like 'get single container repository storage move'
+
+ context 'non-existent container' do
+ let(:container_id) { non_existing_record_id }
+ let(:repository_storage_move_id) { storage_move.id }
+
+ it 'returns not found' do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
end
@@ -109,7 +132,8 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
end
describe "POST /#{container_type}/:id/repository_storage_moves" do
- let(:url) { "/#{container_type}/#{container.id}/repository_storage_moves" }
+ let(:container_id) { container.id }
+ let(:url) { "/#{container_type}/#{container_id}/repository_storage_moves" }
let(:destination_storage_name) { 'test_second_storage' }
def create_container_repository_storage_move
@@ -154,6 +178,16 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
expect(json_response['destination_storage_name']).to be_present
end
end
+
+ context 'when container does not exist' do
+ let(:container_id) { non_existing_record_id }
+
+ it 'returns not found' do
+ create_container_repository_storage_move
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
describe "POST /#{container_type.singularize}_repository_storage_moves" do
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
index 460e8d57a2b..b5139bd8c99 100644
--- a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
@@ -13,6 +13,9 @@ RSpec.shared_examples 'resolvable discussions API' do |parent_type, noteable_typ
end
it "unresolves discussion if resolved is false" do
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_unresolve_thread_action).with(user: user)
+
put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
"discussions/#{note.discussion_id}", user), params: { resolved: false }