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:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-09-16 00:09:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-09-16 00:09:35 +0300
commit7dc8bd3c16a6f8367fdee691711d3313e2efc3c6 (patch)
tree86108a4c223fd6f0d0c461446e243c413723d1f0 /spec/requests/api
parentf784f7d3b19fe80834240bde23d1300accb01118 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/requests/api')
-rw-r--r--spec/requests/api/conan_instance_packages_spec.rb152
-rw-r--r--spec/requests/api/conan_packages_spec.rb986
-rw-r--r--spec/requests/api/conan_project_packages_spec.rb152
-rw-r--r--spec/requests/api/projects_spec.rb83
4 files changed, 343 insertions, 1030 deletions
diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb
new file mode 100644
index 00000000000..817530f0bad
--- /dev/null
+++ b/spec/requests/api/conan_instance_packages_spec.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::ConanInstancePackages do
+ include_context 'conan api setup'
+
+ describe 'GET /api/v4/packages/conan/v1/ping' do
+ let_it_be(:url) { '/packages/conan/v1/ping' }
+
+ it_behaves_like 'conan ping endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/search' do
+ let_it_be(:url) { '/packages/conan/v1/conans/search' }
+
+ it_behaves_like 'conan search endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/users/authenticate' do
+ let_it_be(:url) { '/packages/conan/v1/users/authenticate' }
+
+ it_behaves_like 'conan authenticate endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/users/check_credentials' do
+ let_it_be(:url) { "/packages/conan/v1/users/check_credentials" }
+
+ it_behaves_like 'conan check_credentials endpoint'
+ end
+
+ context 'recipe endpoints' do
+ include_context 'conan recipe endpoints'
+
+ let(:project_id) { 9999 }
+ let(:url_prefix) { "#{Settings.gitlab.base_url}/api/v4" }
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
+ let(:recipe_path) { package.conan_recipe_path }
+ let(:url) { "/packages/conan/v1/conans/#{recipe_path}" }
+
+ it_behaves_like 'recipe snapshot endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference' do
+ let(:recipe_path) { package.conan_recipe_path }
+ let(:url) { "/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}" }
+
+ it_behaves_like 'package snapshot endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/digest' do
+ subject { get api("/packages/conan/v1/conans/#{recipe_path}/digest"), headers: headers }
+
+ it_behaves_like 'recipe download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls' do
+ subject { get api("/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/download_urls"), headers: headers }
+
+ it_behaves_like 'package download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/download_urls' do
+ subject { get api("/packages/conan/v1/conans/#{recipe_path}/download_urls"), headers: headers }
+
+ it_behaves_like 'recipe download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/digest' do
+ subject { get api("/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/digest"), headers: headers }
+
+ it_behaves_like 'package download_urls endpoint'
+ end
+
+ describe 'POST /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/upload_urls' do
+ subject { post api("/packages/conan/v1/conans/#{recipe_path}/upload_urls"), params: params.to_json, headers: headers }
+
+ it_behaves_like 'recipe upload_urls endpoint'
+ end
+
+ describe 'POST /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls' do
+ subject { post api("/packages/conan/v1/conans/#{recipe_path}/packages/123456789/upload_urls"), params: params.to_json, headers: headers }
+
+ it_behaves_like 'package upload_urls endpoint'
+ end
+
+ describe 'DELETE /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
+ subject { delete api("/packages/conan/v1/conans/#{recipe_path}"), headers: headers}
+
+ it_behaves_like 'delete package endpoint'
+ end
+ end
+
+ context 'file download endpoints' do
+ include_context 'conan file download endpoints'
+
+ describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
+:recipe_revision/export/:file_name' do
+ subject do
+ get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
+ headers: headers
+ end
+
+ it_behaves_like 'recipe file download endpoint'
+ it_behaves_like 'project not found by recipe'
+ end
+
+ describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
+:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
+ subject do
+ get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
+ headers: headers
+ end
+
+ it_behaves_like 'package file download endpoint'
+ it_behaves_like 'project not found by recipe'
+ end
+ end
+
+ context 'file upload endpoints' do
+ include_context 'conan file upload endpoints'
+
+ describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize' do
+ let(:file_name) { 'conanfile.py' }
+
+ subject { put api("/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}/authorize"), headers: headers_with_token }
+
+ it_behaves_like 'workhorse authorize endpoint'
+ end
+
+ describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name/authorize' do
+ let(:file_name) { 'conaninfo.txt' }
+
+ subject { put api("/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}/authorize"), headers: headers_with_token }
+
+ it_behaves_like 'workhorse authorize endpoint'
+ end
+
+ describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
+ let(:url) { "/api/v4/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}" }
+
+ it_behaves_like 'workhorse recipe file upload endpoint'
+ end
+
+ describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name' do
+ let(:url) { "/api/v4/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}" }
+
+ it_behaves_like 'workhorse package file upload endpoint'
+ end
+ end
+end
diff --git a/spec/requests/api/conan_packages_spec.rb b/spec/requests/api/conan_packages_spec.rb
deleted file mode 100644
index ab37c361618..00000000000
--- a/spec/requests/api/conan_packages_spec.rb
+++ /dev/null
@@ -1,986 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe API::ConanPackages do
- include WorkhorseHelpers
- include HttpBasicAuthHelpers
- include PackagesManagerApiSpecHelpers
-
- let(:package) { create(:conan_package) }
- let_it_be(:personal_access_token) { create(:personal_access_token) }
- let_it_be(:user) { personal_access_token.user }
- let(:project) { package.project }
-
- let(:base_secret) { SecureRandom.base64(64) }
- let(:auth_token) { personal_access_token.token }
- let(:job) { create(:ci_build, user: user, status: :running) }
- let(:job_token) { job.token }
- let(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
- let(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) }
-
- let(:headers) do
- { 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials('foo', auth_token) }
- end
-
- let(:jwt_secret) do
- OpenSSL::HMAC.hexdigest(
- OpenSSL::Digest::SHA256.new,
- base_secret,
- Gitlab::ConanToken::HMAC_KEY
- )
- end
-
- before do
- project.add_developer(user)
- allow(Settings).to receive(:attr_encrypted_db_key_base).and_return(base_secret)
- end
-
- describe 'GET /api/v4/packages/conan/v1/ping' do
- it 'responds with 401 Unauthorized when no token provided' do
- get api('/packages/conan/v1/ping')
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'responds with 200 OK when valid token is provided' do
- jwt = build_jwt(personal_access_token)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['X-Conan-Server-Capabilities']).to eq("")
- end
-
- it 'responds with 200 OK when valid job token is provided' do
- jwt = build_jwt_from_job(job)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['X-Conan-Server-Capabilities']).to eq("")
- end
-
- it 'responds with 200 OK when valid deploy token is provided' do
- jwt = build_jwt_from_deploy_token(deploy_token)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['X-Conan-Server-Capabilities']).to eq("")
- end
-
- it 'responds with 401 Unauthorized when invalid access token ID is provided' do
- jwt = build_jwt(double(id: 12345), user_id: personal_access_token.user_id)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'responds with 401 Unauthorized when invalid user is provided' do
- jwt = build_jwt(personal_access_token, user_id: 12345)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'responds with 401 Unauthorized when the provided JWT is signed with different secret' do
- jwt = build_jwt(personal_access_token, secret: SecureRandom.base64(32))
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'responds with 401 Unauthorized when invalid JWT is provided' do
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header('invalid-jwt')
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'responds with 401 Unauthorized when the job is not running' do
- job.update!(status: :failed)
- jwt = build_jwt_from_job(job)
- get api('/packages/conan/v1/ping'), headers: build_token_auth_header(jwt.encoded)
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- context 'packages feature disabled' do
- it 'responds with 404 Not Found' do
- stub_packages_setting(enabled: false)
- get api('/packages/conan/v1/ping')
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/search' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
-
- get api('/packages/conan/v1/conans/search'), headers: headers, params: params
- end
-
- subject { json_response['results'] }
-
- context 'returns packages with a matching name' do
- let(:params) { { q: package.conan_recipe } }
-
- it { is_expected.to contain_exactly(package.conan_recipe) }
- end
-
- context 'returns packages using a * wildcard' do
- let(:params) { { q: "#{package.name[0, 3]}*" } }
-
- it { is_expected.to contain_exactly(package.conan_recipe) }
- end
-
- context 'does not return non-matching packages' do
- let(:params) { { q: "foo" } }
-
- it { is_expected.to be_blank }
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/users/authenticate' do
- subject { get api('/packages/conan/v1/users/authenticate'), headers: headers }
-
- context 'when using invalid token' do
- let(:auth_token) { 'invalid_token' }
-
- it 'responds with 401' do
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
-
- context 'when valid JWT access token is provided' do
- it 'responds with 200' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- it 'token has valid validity time' do
- freeze_time do
- subject
-
- payload = JSONWebToken::HMACToken.decode(
- response.body, jwt_secret).first
- expect(payload['access_token']).to eq(personal_access_token.id)
- expect(payload['user_id']).to eq(personal_access_token.user_id)
-
- duration = payload['exp'] - payload['iat']
- expect(duration).to eq(1.hour)
- end
- end
- end
-
- context 'with valid job token' do
- let(:auth_token) { job_token }
-
- it 'responds with 200' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'with valid deploy token' do
- let(:auth_token) { deploy_token.token }
-
- it 'responds with 200' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/users/check_credentials' do
- it 'responds with a 200 OK with PAT' do
- get api('/packages/conan/v1/users/check_credentials'), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- end
-
- context 'with job token' do
- let(:auth_token) { job_token }
-
- it 'responds with a 200 OK with job token' do
- get api('/packages/conan/v1/users/check_credentials'), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- context 'with deploy token' do
- let(:auth_token) { deploy_token.token }
-
- it 'responds with a 200 OK with job token' do
- get api('/packages/conan/v1/users/check_credentials'), headers: headers
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
-
- it 'responds with a 401 Unauthorized when an invalid token is used' do
- get api('/packages/conan/v1/users/check_credentials'), headers: build_token_auth_header('invalid-token')
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
-
- shared_examples 'rejects invalid recipe' do
- context 'with invalid recipe path' do
- let(:recipe_path) { '../../foo++../..' }
-
- it 'returns 400' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
- end
-
- shared_examples 'rejects invalid file_name' do |invalid_file_name|
- let(:file_name) { invalid_file_name }
-
- context 'with invalid file_name' do
- it 'returns 400' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
- end
-
- shared_examples 'rejects recipe for invalid project' do
- context 'with invalid recipe path' do
- let(:recipe_path) { 'aa/bb/not-existing-project/ccc' }
-
- it 'returns forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
- end
-
- shared_examples 'rejects recipe for not found package' do
- context 'with invalid recipe path' do
- let(:recipe_path) do
- 'aa/bb/%{project}/ccc' % { project: ::Packages::Conan::Metadatum.package_username_from(full_path: project.full_path) }
- end
-
- it 'returns not found' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
- end
-
- shared_examples 'empty recipe for not found package' do
- context 'with invalid recipe url' do
- let(:recipe_path) do
- 'aa/bb/%{project}/ccc' % { project: ::Packages::Conan::Metadatum.package_username_from(full_path: project.full_path) }
- end
-
- it 'returns not found' do
- allow(::Packages::Conan::PackagePresenter).to receive(:new)
- .with(
- nil,
- user,
- project,
- any_args
- ).and_return(presenter)
- allow(presenter).to receive(:recipe_snapshot) { {} }
- allow(presenter).to receive(:package_snapshot) { {} }
-
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to eq("{}")
- end
- end
- end
-
- shared_examples 'not selecting a package with the wrong type' do
- context 'with a nuget package with same name and version' do
- let(:conan_username) { ::Packages::Conan::Metadatum.package_username_from(full_path: project.full_path) }
- let(:wrong_package) { create(:nuget_package, name: "wrong", version: '1.0.0', project: project) }
- let(:recipe_path) { "#{wrong_package.name}/#{wrong_package.version}/#{conan_username}/foo" }
-
- it 'calls the presenter with a nil package' do
- expect(::Packages::Conan::PackagePresenter).to receive(:new)
- .with(nil, user, project, any_args)
-
- subject
- end
- end
- end
-
- shared_examples 'recipe download_urls' do
- let(:recipe_path) { package.conan_recipe_path }
-
- it 'returns the download_urls for the recipe files' do
- expected_response = {
- 'conanfile.py' => "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
- 'conanmanifest.txt' => "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
- }
-
- allow(presenter).to receive(:recipe_urls) { expected_response }
-
- subject
-
- expect(json_response).to eq(expected_response)
- end
-
- it_behaves_like 'not selecting a package with the wrong type'
- end
-
- shared_examples 'package download_urls' do
- let(:recipe_path) { package.conan_recipe_path }
-
- it 'returns the download_urls for the package files' do
- expected_response = {
- 'conaninfo.txt' => "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
- 'conanmanifest.txt' => "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conanmanifest.txt",
- 'conan_package.tgz' => "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conan_package.tgz"
- }
-
- allow(presenter).to receive(:package_urls) { expected_response }
-
- subject
-
- expect(json_response).to eq(expected_response)
- end
-
- it_behaves_like 'not selecting a package with the wrong type'
- end
-
- context 'recipe endpoints' do
- let(:jwt) { build_jwt(personal_access_token) }
- let(:headers) { build_token_auth_header(jwt.encoded) }
- let(:conan_package_reference) { '123456789' }
- let(:presenter) { double('::Packages::Conan::PackagePresenter') }
-
- before do
- allow(::Packages::Conan::PackagePresenter).to receive(:new)
- .with(package, user, package.project, any_args)
- .and_return(presenter)
- end
-
- shared_examples 'rejects invalid upload_url params' do
- context 'with unaccepted json format' do
- let(:params) { %w[foo bar] }
-
- it 'returns 400' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
- end
-
- shared_examples 'successful response when using Unicorn' do
- context 'on Unicorn', :unicorn do
- it 'returns successfully' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- end
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
- let(:recipe_path) { package.conan_recipe_path }
-
- subject { get api("/packages/conan/v1/conans/#{recipe_path}"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'empty recipe for not found package'
-
- context 'with existing package' do
- it 'returns a hash of files with their md5 hashes' do
- expected_response = {
- 'conanfile.py' => 'md5hash1',
- 'conanmanifest.txt' => 'md5hash2'
- }
-
- allow(presenter).to receive(:recipe_snapshot) { expected_response }
-
- subject
-
- expect(json_response).to eq(expected_response)
- end
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference' do
- let(:recipe_path) { package.conan_recipe_path }
-
- subject { get api("/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'empty recipe for not found package'
-
- context 'with existing package' do
- it 'returns a hash of md5 values for the files' do
- expected_response = {
- 'conaninfo.txt' => "md5hash1",
- 'conanmanifest.txt' => "md5hash2",
- 'conan_package.tgz' => "md5hash3"
- }
-
- allow(presenter).to receive(:package_snapshot) { expected_response }
-
- subject
-
- expect(json_response).to eq(expected_response)
- end
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/digest' do
- subject { get api("/packages/conan/v1/conans/#{recipe_path}/digest"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'recipe download_urls'
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls' do
- subject { get api("/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/download_urls"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'package download_urls'
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/download_urls' do
- subject { get api("/packages/conan/v1/conans/#{recipe_path}/download_urls"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'recipe download_urls'
- end
-
- describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/digest' do
- subject { get api("/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/digest"), headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects recipe for invalid project'
- it_behaves_like 'package download_urls'
- end
-
- describe 'POST /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/upload_urls' do
- let(:recipe_path) { package.conan_recipe_path }
-
- let(:params) do
- { 'conanfile.py': 24,
- 'conanmanifest.txt': 123 }
- end
-
- subject { post api("/packages/conan/v1/conans/#{recipe_path}/upload_urls"), params: params.to_json, headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid upload_url params'
- it_behaves_like 'successful response when using Unicorn'
-
- it 'returns a set of upload urls for the files requested' do
- subject
-
- expected_response = {
- 'conanfile.py': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
- 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
- }
-
- expect(response.body).to eq(expected_response.to_json)
- end
-
- context 'with conan_sources and conan_export files' do
- let(:params) do
- { 'conan_sources.tgz': 345,
- 'conan_export.tgz': 234,
- 'conanmanifest.txt': 123 }
- end
-
- it 'returns upload urls for the additional files' do
- subject
-
- expected_response = {
- 'conan_sources.tgz': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_sources.tgz",
- 'conan_export.tgz': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_export.tgz",
- 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
- }
-
- expect(response.body).to eq(expected_response.to_json)
- end
- end
-
- context 'with an invalid file' do
- let(:params) do
- { 'invalid_file.txt': 10,
- 'conanmanifest.txt': 123 }
- end
-
- it 'does not return the invalid file as an upload_url' do
- subject
-
- expected_response = {
- 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
- }
-
- expect(response.body).to eq(expected_response.to_json)
- end
- end
- end
-
- describe 'POST /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls' do
- let(:recipe_path) { package.conan_recipe_path }
-
- let(:params) do
- { 'conaninfo.txt': 24,
- 'conanmanifest.txt': 123,
- 'conan_package.tgz': 523 }
- end
-
- subject { post api("/packages/conan/v1/conans/#{recipe_path}/packages/123456789/upload_urls"), params: params.to_json, headers: headers }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid upload_url params'
- it_behaves_like 'successful response when using Unicorn'
-
- it 'returns a set of upload urls for the files requested' do
- expected_response = {
- 'conaninfo.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
- 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conanmanifest.txt",
- 'conan_package.tgz': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conan_package.tgz"
- }
-
- subject
-
- expect(response.body).to eq(expected_response.to_json)
- end
-
- context 'with invalid files' do
- let(:params) do
- { 'conaninfo.txt': 24,
- 'invalid_file.txt': 10 }
- end
-
- it 'returns upload urls only for the valid requested files' do
- expected_response = {
- 'conaninfo.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt"
- }
-
- subject
-
- expect(response.body).to eq(expected_response.to_json)
- end
- end
- end
-
- describe 'DELETE /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
- let(:recipe_path) { package.conan_recipe_path }
-
- subject { delete api("/packages/conan/v1/conans/#{recipe_path}"), headers: headers}
-
- it_behaves_like 'rejects invalid recipe'
-
- it 'returns unauthorized for users without valid permission' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
- context 'with delete permissions' do
- before do
- project.add_maintainer(user)
- end
-
- it_behaves_like 'a gitlab tracking event', described_class.name, 'delete_package'
-
- it 'deletes a package' do
- expect { subject }.to change { Packages::Package.count }.from(2).to(1)
- end
- end
- end
- end
-
- context 'file endpoints' do
- let(:jwt) { build_jwt(personal_access_token) }
- let(:headers) { build_token_auth_header(jwt.encoded) }
- let(:recipe_path) { package.conan_recipe_path }
-
- shared_examples 'denies download with no token' do
- context 'with no private token' do
- let(:headers) { {} }
-
- it 'returns 400' do
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
- end
-
- shared_examples 'a public project with packages' do
- it 'returns the file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
- end
-
- shared_examples 'an internal project with packages' do
- before do
- project.team.truncate
- project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- end
-
- it_behaves_like 'denies download with no token'
-
- it 'returns the file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
- end
-
- shared_examples 'a private project with packages' do
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- end
-
- it_behaves_like 'denies download with no token'
-
- it 'returns the file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('application/octet-stream')
- end
-
- it 'denies download when not enough permissions' do
- project.add_guest(user)
-
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- shared_examples 'a project is not found' do
- let(:recipe_path) { 'not/package/for/project' }
-
- it 'returns forbidden' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
-
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/export/:file_name' do
- let(:recipe_file) { package.package_files.find_by(file_name: 'conanfile.py') }
- let(:metadata) { recipe_file.conan_file_metadatum }
-
- subject do
- get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
- headers: headers
- end
-
- it_behaves_like 'a public project with packages'
- it_behaves_like 'an internal project with packages'
- it_behaves_like 'a private project with packages'
- it_behaves_like 'a project is not found'
- end
-
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
- let(:package_file) { package.package_files.find_by(file_name: 'conaninfo.txt') }
- let(:metadata) { package_file.conan_file_metadatum }
-
- subject do
- get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
- headers: headers
- end
-
- it_behaves_like 'a public project with packages'
- it_behaves_like 'an internal project with packages'
- it_behaves_like 'a private project with packages'
- it_behaves_like 'a project is not found'
-
- context 'tracking the conan_package.tgz download' do
- let(:package_file) { package.package_files.find_by(file_name: ::Packages::Conan::FileMetadatum::PACKAGE_BINARY) }
-
- it_behaves_like 'a gitlab tracking event', described_class.name, 'pull_package'
- end
- end
- end
-
- context 'file uploads' do
- let(:jwt) { build_jwt(personal_access_token) }
- let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
- let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
- let(:headers_with_token) { build_token_auth_header(jwt.encoded).merge(workhorse_header) }
- let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz"}
-
- shared_examples 'uploads a package file' do
- context 'file size above maximum limit' do
- before do
- params['file.size'] = project.actual_limits.conan_max_file_size + 1
- end
-
- it 'handles as a local file' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
-
- context 'with object storage disabled' do
- context 'without a file from workhorse' do
- let(:params) { { file: nil } }
-
- it 'rejects the request' do
- subject
-
- expect(response).to have_gitlab_http_status(:bad_request)
- end
- end
-
- context 'with a file' do
- it_behaves_like 'package workhorse uploads'
- end
-
- context 'without a token' do
- it 'rejects request without a token' do
- headers_with_token.delete('HTTP_AUTHORIZATION')
-
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
-
- context 'when params from workhorse are correct' do
- it 'creates package and stores package file' do
- expect { subject }
- .to change { project.packages.count }.by(1)
- .and change { Packages::PackageFile.count }.by(1)
-
- expect(response).to have_gitlab_http_status(:ok)
-
- package_file = project.packages.last.package_files.reload.last
- expect(package_file.file_name).to eq(params[:file].original_filename)
- end
-
- it "doesn't attempt to migrate file to object storage" do
- expect(ObjectStorage::BackgroundMoveWorker).not_to receive(:perform_async)
-
- subject
- end
- end
- end
-
- context 'with object storage enabled' do
- context 'and direct upload enabled' do
- let!(:fog_connection) do
- stub_package_file_object_storage(direct_upload: true)
- end
-
- let(:tmp_object) do
- fog_connection.directories.new(key: 'packages').files.create(
- key: "tmp/uploads/#{file_name}",
- body: 'content'
- )
- end
-
- let(:fog_file) { fog_to_uploaded_file(tmp_object) }
-
- ['123123', '../../123123'].each do |remote_id|
- context "with invalid remote_id: #{remote_id}" do
- let(:params) do
- {
- file: fog_file,
- 'file.remote_id' => remote_id
- }
- end
-
- it 'responds with status 403' do
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
- end
- end
-
- context 'with valid remote_id' do
- let(:params) do
- {
- file: fog_file,
- 'file.remote_id' => file_name
- }
- end
-
- it 'creates package and stores package file' do
- expect { subject }
- .to change { project.packages.count }.by(1)
- .and change { Packages::PackageFile.count }.by(1)
-
- expect(response).to have_gitlab_http_status(:ok)
-
- package_file = project.packages.last.package_files.reload.last
- expect(package_file.file_name).to eq(params[:file].original_filename)
- expect(package_file.file.read).to eq('content')
- end
- end
- end
-
- it_behaves_like 'background upload schedules a file migration'
- end
- end
-
- shared_examples 'workhorse authorization' do
- it 'authorizes posting package with a valid token' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- end
-
- it 'rejects request without a valid token' do
- headers_with_token['HTTP_AUTHORIZATION'] = 'foo'
-
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
-
- it 'rejects request without a valid permission' do
- project.add_guest(user)
-
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
- it 'rejects requests that bypassed gitlab-workhorse' do
- headers_with_token.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
-
- subject
-
- expect(response).to have_gitlab_http_status(:forbidden)
- end
-
- context 'when using remote storage' do
- context 'when direct upload is enabled' do
- before do
- stub_package_file_object_storage(enabled: true, direct_upload: true)
- end
-
- it 'responds with status 200, location of package remote store and object details' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response).not_to have_key('TempPath')
- expect(json_response['RemoteObject']).to have_key('ID')
- expect(json_response['RemoteObject']).to have_key('GetURL')
- expect(json_response['RemoteObject']).to have_key('StoreURL')
- expect(json_response['RemoteObject']).to have_key('DeleteURL')
- expect(json_response['RemoteObject']).not_to have_key('MultipartUpload')
- end
- end
-
- context 'when direct upload is disabled' do
- before do
- stub_package_file_object_storage(enabled: true, direct_upload: false)
- end
-
- it 'handles as a local file' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- expect(json_response['TempPath']).to eq(::Packages::PackageFileUploader.workhorse_local_upload_path)
- expect(json_response['RemoteObject']).to be_nil
- end
- end
- end
- end
-
- describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize' do
- let(:file_name) { 'conanfile.py' }
-
- subject { put api("/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}/authorize"), headers: headers_with_token }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid file_name', 'conanfile.py.git%2fgit-upload-pack'
- it_behaves_like 'workhorse authorization'
- end
-
- describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name/authorize' do
- let(:file_name) { 'conaninfo.txt' }
-
- subject { put api("/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}/authorize"), headers: headers_with_token }
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid file_name', 'conaninfo.txttest'
- it_behaves_like 'workhorse authorization'
- end
-
- describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
- let(:file_name) { 'conanfile.py' }
- let(:params) { { file: temp_file(file_name) } }
-
- subject do
- workhorse_finalize(
- "/api/v4/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}",
- method: :put,
- file_key: :file,
- params: params,
- send_rewritten_field: true,
- headers: headers_with_token
- )
- end
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid file_name', 'conanfile.py.git%2fgit-upload-pack'
- it_behaves_like 'uploads a package file'
- end
-
- describe 'PUT /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name' do
- let(:file_name) { 'conaninfo.txt' }
- let(:params) { { file: temp_file(file_name) } }
-
- subject do
- workhorse_finalize(
- "/api/v4/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}",
- method: :put,
- file_key: :file,
- params: params,
- headers: headers_with_token,
- send_rewritten_field: true
- )
- end
-
- it_behaves_like 'rejects invalid recipe'
- it_behaves_like 'rejects invalid file_name', 'conaninfo.txttest'
- it_behaves_like 'uploads a package file'
-
- context 'tracking the conan_package.tgz upload' do
- let(:file_name) { ::Packages::Conan::FileMetadatum::PACKAGE_BINARY }
-
- it_behaves_like 'a gitlab tracking event', described_class.name, 'push_package'
- end
- end
- end
-end
diff --git a/spec/requests/api/conan_project_packages_spec.rb b/spec/requests/api/conan_project_packages_spec.rb
new file mode 100644
index 00000000000..fefaf9790b1
--- /dev/null
+++ b/spec/requests/api/conan_project_packages_spec.rb
@@ -0,0 +1,152 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::ConanProjectPackages do
+ include_context 'conan api setup'
+
+ let(:project_id) { project.id }
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/ping' do
+ let(:url) { "/projects/#{project.id}/packages/conan/v1/ping" }
+
+ it_behaves_like 'conan ping endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/search' do
+ let(:url) { "/projects/#{project.id}/packages/conan/v1/conans/search" }
+
+ it_behaves_like 'conan search endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/users/authenticate' do
+ let(:url) { "/projects/#{project.id}/packages/conan/v1/users/authenticate" }
+
+ it_behaves_like 'conan authenticate endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/users/check_credentials' do
+ let(:url) { "/projects/#{project.id}/packages/conan/v1/users/check_credentials" }
+
+ it_behaves_like 'conan check_credentials endpoint'
+ end
+
+ context 'recipe endpoints' do
+ include_context 'conan recipe endpoints'
+
+ let(:url_prefix) { "#{Settings.gitlab.base_url}/api/v4/projects/#{project_id}" }
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
+ let(:recipe_path) { package.conan_recipe_path }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}" }
+
+ it_behaves_like 'recipe snapshot endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference' do
+ let(:recipe_path) { package.conan_recipe_path }
+ let(:url) { "/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}" }
+
+ it_behaves_like 'package snapshot endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/digest' do
+ subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/digest"), headers: headers }
+
+ it_behaves_like 'recipe download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/download_urls' do
+ subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/download_urls"), headers: headers }
+
+ it_behaves_like 'package download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/download_urls' do
+ subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/download_urls"), headers: headers }
+
+ it_behaves_like 'recipe download_urls endpoint'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/digest' do
+ subject { get api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/#{conan_package_reference}/digest"), headers: headers }
+
+ it_behaves_like 'package download_urls endpoint'
+ end
+
+ describe 'POST /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/upload_urls' do
+ subject { post api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/upload_urls"), params: params.to_json, headers: headers }
+
+ it_behaves_like 'recipe upload_urls endpoint'
+ end
+
+ describe 'POST /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls' do
+ subject { post api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}/packages/123456789/upload_urls"), params: params.to_json, headers: headers }
+
+ it_behaves_like 'package upload_urls endpoint'
+ end
+
+ describe 'DELETE /api/v4/projects/:id/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
+ subject { delete api("/projects/#{project_id}/packages/conan/v1/conans/#{recipe_path}"), headers: headers}
+
+ it_behaves_like 'delete package endpoint'
+ end
+ end
+
+ context 'file download endpoints' do
+ include_context 'conan file download endpoints'
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
+:recipe_revision/export/:file_name' do
+ subject do
+ get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
+ headers: headers
+ end
+
+ it_behaves_like 'recipe file download endpoint'
+ it_behaves_like 'project not found by project id'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
+:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
+ subject do
+ get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
+ headers: headers
+ end
+
+ it_behaves_like 'package file download endpoint'
+ it_behaves_like 'project not found by project id'
+ end
+ end
+
+ context 'file upload endpoints' do
+ include_context 'conan file upload endpoints'
+
+ describe 'PUT /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name/authorize' do
+ let(:file_name) { 'conanfile.py' }
+
+ subject { put api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}/authorize"), headers: headers_with_token }
+
+ it_behaves_like 'workhorse authorize endpoint'
+ end
+
+ describe 'PUT /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name/authorize' do
+ let(:file_name) { 'conaninfo.txt' }
+
+ subject { put api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}/authorize"), headers: headers_with_token }
+
+ it_behaves_like 'workhorse authorize endpoint'
+ end
+
+ describe 'PUT /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
+ let(:url) { "/api/v4/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/0/export/#{file_name}" }
+
+ it_behaves_like 'workhorse recipe file upload endpoint'
+ end
+
+ describe 'PUT /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:conan_package_reference/:package_revision/:file_name' do
+ let(:url) { "/api/v4/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/0/package/123456789/0/#{file_name}" }
+
+ it_behaves_like 'workhorse package file upload endpoint'
+ end
+ end
+end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index df95835ae94..b4705c4cc55 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -49,15 +49,15 @@ end
RSpec.describe API::Projects do
include ProjectForksHelper
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
- let(:user3) { create(:user) }
- let(:admin) { create(:admin) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
- let(:project2) { create(:project, namespace: user.namespace) }
- let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
- let(:user4) { create(:user, username: 'user.with.dot') }
- let(:project3) do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:user3) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:project, reload: true) { create(:project, :repository, namespace: user.namespace) }
+ let_it_be(:project2, reload: true) { create(:project, namespace: user.namespace) }
+ let_it_be(:project_member) { create(:project_member, :developer, user: user3, project: project) }
+ let_it_be(:user4) { create(:user, username: 'user.with.dot') }
+ let_it_be(:project3, reload: true) do
create(:project,
:private,
:repository,
@@ -71,14 +71,14 @@ RSpec.describe API::Projects do
snippets_enabled: false)
end
- let(:project_member2) do
+ let_it_be(:project_member2) do
create(:project_member,
user: user4,
project: project3,
access_level: ProjectMember::MAINTAINER)
end
- let(:project4) do
+ let_it_be(:project4, reload: true) do
create(:project,
name: 'third_project',
path: 'third_project',
@@ -86,6 +86,8 @@ RSpec.describe API::Projects do
namespace: user4.namespace)
end
+ let(:user_projects) { [public_project, project, project2, project3] }
+
shared_context 'with language detection' do
let(:ruby) { create(:programming_language, name: 'Ruby') }
let(:javascript) { create(:programming_language, name: 'JavaScript') }
@@ -146,14 +148,7 @@ RSpec.describe API::Projects do
end
end
- let!(:public_project) { create(:project, :public, name: 'public_project') }
-
- before do
- project
- project2
- project3
- project4
- end
+ let_it_be(:public_project) { create(:project, :public, name: 'public_project') }
context 'when unauthenticated' do
it_behaves_like 'projects response' do
@@ -171,7 +166,7 @@ RSpec.describe API::Projects do
it_behaves_like 'projects response' do
let(:filter) { {} }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3] }
+ let(:projects) { user_projects }
end
it_behaves_like 'projects response without N + 1 queries' do
@@ -257,7 +252,7 @@ RSpec.describe API::Projects do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- statistics = json_response.first['statistics']
+ statistics = json_response.find { |p| p['id'] == project.id }['statistics']
expect(statistics).to be_present
expect(statistics).to include('commit_count', 'storage_size', 'repository_size', 'wiki_size', 'lfs_objects_size', 'job_artifacts_size', 'snippets_size')
end
@@ -386,14 +381,14 @@ RSpec.describe API::Projects do
it_behaves_like 'projects response' do
let(:filter) { { id_after: project2.id } }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3].select { |p| p.id > project2.id } }
+ let(:projects) { user_projects.select { |p| p.id > project2.id } }
end
context 'regression: empty string is ignored' do
it_behaves_like 'projects response' do
let(:filter) { { id_after: '' } }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3] }
+ let(:projects) { user_projects }
end
end
end
@@ -402,14 +397,14 @@ RSpec.describe API::Projects do
it_behaves_like 'projects response' do
let(:filter) { { id_before: project2.id } }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id } }
+ let(:projects) { user_projects.select { |p| p.id < project2.id } }
end
context 'regression: empty string is ignored' do
it_behaves_like 'projects response' do
let(:filter) { { id_before: '' } }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3] }
+ let(:projects) { user_projects }
end
end
end
@@ -418,7 +413,7 @@ RSpec.describe API::Projects do
it_behaves_like 'projects response' do
let(:filter) { { id_before: project2.id, id_after: public_project.id } }
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id && p.id > public_project.id } }
+ let(:projects) { user_projects.select { |p| p.id < project2.id && p.id > public_project.id } }
end
end
@@ -481,7 +476,7 @@ RSpec.describe API::Projects 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.first['id']).to eq(project3.id)
+ expect(json_response.map { |p| p['id'] }).to eq(user_projects.map(&:id).sort.reverse)
end
end
@@ -501,7 +496,6 @@ RSpec.describe API::Projects do
let(:public_project) { create(:project, :public) }
before do
- project_member
user3.update!(starred_projects: [project, project2, project3, public_project])
end
@@ -642,7 +636,6 @@ RSpec.describe API::Projects do
context 'non-admin user' do
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3] }
it 'returns projects ordered normally' do
get api('/projects', current_user), params: { order_by: order_by }
@@ -650,7 +643,7 @@ RSpec.describe API::Projects 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.map { |project| project['id'] }).to eq(projects.map(&:id).reverse)
+ expect(json_response.map { |project| project['id'] }).to eq(user_projects.map(&:id).sort.reverse)
end
end
end
@@ -686,7 +679,8 @@ RSpec.describe API::Projects do
context 'with keyset pagination' do
let(:current_user) { user }
- let(:projects) { [public_project, project, project2, project3] }
+ let(:first_project_id) { user_projects.map(&:id).min }
+ let(:last_project_id) { user_projects.map(&:id).max }
context 'headers and records' do
let(:params) { { pagination: 'keyset', order_by: :id, sort: :asc, per_page: 1 } }
@@ -696,11 +690,11 @@ RSpec.describe API::Projects do
expect(response.header).to include('Links')
expect(response.header['Links']).to include('pagination=keyset')
- expect(response.header['Links']).to include("id_after=#{public_project.id}")
+ expect(response.header['Links']).to include("id_after=#{first_project_id}")
expect(response.header).to include('Link')
expect(response.header['Link']).to include('pagination=keyset')
- expect(response.header['Link']).to include("id_after=#{public_project.id}")
+ expect(response.header['Link']).to include("id_after=#{first_project_id}")
end
it 'contains only the first project with per_page = 1' do
@@ -708,7 +702,7 @@ RSpec.describe API::Projects do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
- expect(json_response.map { |p| p['id'] }).to contain_exactly(public_project.id)
+ expect(json_response.map { |p| p['id'] }).to contain_exactly(first_project_id)
end
it 'still includes a link if the end has reached and there is no more data after this page' do
@@ -752,11 +746,11 @@ RSpec.describe API::Projects do
expect(response.header).to include('Links')
expect(response.header['Links']).to include('pagination=keyset')
- expect(response.header['Links']).to include("id_before=#{project3.id}")
+ expect(response.header['Links']).to include("id_before=#{last_project_id}")
expect(response.header).to include('Link')
expect(response.header['Link']).to include('pagination=keyset')
- expect(response.header['Link']).to include("id_before=#{project3.id}")
+ expect(response.header['Link']).to include("id_before=#{last_project_id}")
end
it 'contains only the last project with per_page = 1' do
@@ -764,7 +758,7 @@ RSpec.describe API::Projects do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
- expect(json_response.map { |p| p['id'] }).to contain_exactly(project3.id)
+ expect(json_response.map { |p| p['id'] }).to contain_exactly(last_project_id)
end
end
@@ -793,7 +787,7 @@ RSpec.describe API::Projects do
ids += Gitlab::Json.parse(response.body).map { |p| p['id'] }
end
- expect(ids).to contain_exactly(*projects.map(&:id))
+ expect(ids).to contain_exactly(*user_projects.map(&:id))
end
end
end
@@ -814,7 +808,7 @@ RSpec.describe API::Projects do
.to change { Project.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
- project = Project.first
+ project = Project.last
expect(project.name).to eq('Foo Project')
expect(project.path).to eq('foo-project')
@@ -825,7 +819,7 @@ RSpec.describe API::Projects do
.to change { Project.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
- project = Project.first
+ project = Project.last
expect(project.name).to eq('foo_project')
expect(project.path).to eq('foo_project')
@@ -836,7 +830,7 @@ RSpec.describe API::Projects do
.to change { Project.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
- project = Project.first
+ project = Project.last
expect(project.name).to eq('Foo Project')
expect(project.path).to eq('path-project-Foo')
@@ -1985,7 +1979,8 @@ RSpec.describe API::Projects do
context 'when authenticated' do
context 'valid request' do
it_behaves_like 'project users response' do
- let(:current_user) { user }
+ let(:project) { project4 }
+ let(:current_user) { user4 }
end
end
@@ -2011,8 +2006,8 @@ RSpec.describe API::Projects do
get api("/projects/#{project.id}/users?skip_users=#{user.id}", user)
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.size).to eq(1)
- expect(json_response[0]['id']).to eq(other_user.id)
+ expect(json_response.size).to eq(2)
+ expect(json_response.map { |m| m['id'] }).not_to include(user.id)
end
end
end