Commit 904b5e56 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '10io-nuget-package-content-service' into 'master'

Nuget package content service

See merge request gitlab-org/gitlab!24290
parents 1572d4fa 42d66675
......@@ -4,14 +4,15 @@ module Packages
class PackageFinder
MAX_PACKAGES_COUNT = 50
def initialize(project, package_name:, package_version: nil)
def initialize(project, package_name:, package_version: nil, limit: MAX_PACKAGES_COUNT)
@project = project
@package_name = package_name
@package_version = package_version
@limit = limit
end
def execute
packages.limit_recent(MAX_PACKAGES_COUNT)
packages.limit_recent(@limit)
end
private
......@@ -19,7 +20,9 @@ module Packages
def packages
result = @project.packages
.nuget
.with_name(@package_name)
.has_version
.processed
.with_name_like(@package_name)
result = result.with_version(@package_version) if @package_version.present?
result
end
......
......@@ -28,7 +28,11 @@ class Packages::PackageFileFinder
end
def by_file_name(files)
files.where(file_name: file_name) # rubocop: disable CodeReuse/ActiveRecord
if params[:with_file_name_like]
files.with_file_name_like(file_name)
else
files.with_file_name(file_name)
end
end
def by_conan_file_type(files)
......
......@@ -81,6 +81,10 @@ class Packages::Package < ApplicationRecord
pluck(:name)
end
def self.pluck_versions
pluck(:version)
end
def self.sort_by_attribute(method)
case method.to_s
when 'created_asc' then order_created
......
......@@ -22,6 +22,8 @@ class Packages::PackageFile < ApplicationRecord
validates :file_name, presence: true
scope :recent, -> { order(id: :desc) }
scope :with_file_name, ->(file_name) { where(file_name: file_name) }
scope :with_file_name_like, ->(file_name) { where(arel_table[:file_name].matches(file_name)) }
scope :with_files_stored_locally, -> { where(file_store: ::Packages::PackageFileUploader::Store::LOCAL) }
scope :with_conan_file_metadata, -> { includes(:conan_file_metadatum) }
......
# frozen_string_literal: true
module Packages
module Nuget
class PackagesVersionsPresenter
attr_reader :packages
def initialize(packages)
@packages = packages
end
def versions
@packages.pluck_versions.sort
end
end
end
end
......@@ -55,6 +55,30 @@ module API
header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME)
unauthorized!
end
def find_packages
packages = package_finder.execute
not_found!('Packages') unless packages.exists?
packages
end
def find_package
package = package_finder(package_version: params[:package_version]).execute
.first
not_found!('Package') unless package
package
end
def package_finder(finder_params = {})
::Packages::Nuget::PackageFinder.new(
authorized_user_project,
finder_params.merge(package_name: params[:package_name])
)
end
end
before do
......@@ -130,12 +154,7 @@ module API
detail 'This feature was introduced in GitLab 12.8'
end
get 'index', format: :json do
packages = ::Packages::Nuget::PackageFinder.new(authorized_user_project, package_name: params[:package_name])
.execute
not_found!('Packages') unless packages.exists?
present ::Packages::Nuget::PackagesMetadataPresenter.new(packages),
present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
with: EE::API::Entities::Nuget::PackagesMetadata
end
......@@ -146,32 +165,44 @@ module API
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
end
get '*package_version', format: :json do
package = ::Packages::Nuget::PackageFinder
.new(authorized_user_project, package_name: params[:package_name], package_version: params[:package_version])
.execute
.first
not_found!('Package') unless package
present ::Packages::Nuget::PackageMetadataPresenter.new(package),
present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
with: EE::API::Entities::Nuget::PackageMetadata
end
end
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
desc 'The NuGet Content Service' do
detail 'This feature was introduced in GitLab 12.8'
end
params do
requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
end
namespace '/download/*package_name/*package_version' do
namespace '/download/*package_name' do
before do
authorize_read_package!(authorized_user_project)
end
desc 'The NuGet Content Service - index request' do
detail 'This feature was introduced in GitLab 12.8'
end
get 'index', format: :json do
present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
with: EE::API::Entities::Nuget::PackagesVersions
end
desc 'The NuGet Content Service - content request' do
detail 'This feature was introduced in GitLab 12.8'
end
params do
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
end
get '*package_filename' do
not_found!('package not found') # TODO NUGET API: not implemented yet.
get '*package_version/*package_filename', format: :nupkg do
filename = "#{params[:package_filename]}.#{params[:format]}"
package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
.execute
not_found!('Package') unless package_file
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
present_carrierwave_file!(package_file.file, supports_direct_download: false)
end
end
end
......
......@@ -907,6 +907,10 @@ module EE
expose :count
expose :items, using: EE::API::Entities::Nuget::PackagesMetadataItem
end
class PackagesVersions < Grape::Entity
expose :versions
end
end
class NpmPackage < Grape::Entity
......
......@@ -42,7 +42,7 @@ FactoryBot.define do
package_type { :nuget }
after :create do |package|
create :package_file, :nuget, package: package
create :package_file, :nuget, package: package, file_name: "#{package.name}.#{package.version}.nupkg"
end
end
......
......@@ -3,16 +3,24 @@ require 'spec_helper'
describe Packages::Nuget::PackageFinder do
let_it_be(:package1) { create(:nuget_package) }
let_it_be(:package2) { create(:nuget_package, name: package1.name, version: '2.0.0', project: package1.project) }
let_it_be(:project) { package1.project }
let_it_be(:package2) { create(:nuget_package, name: package1.name, version: '2.0.0', project: project) }
let_it_be(:package3) { create(:nuget_package, name: 'Another.Dummy.Package', project: project) }
let(:package_name) { package1.name }
let(:package_version) { nil }
let(:limit) { 50 }
describe '#execute!' do
subject { described_class.new(project, package_name: package_name, package_version: package_version).execute }
subject { described_class.new(project, package_name: package_name, package_version: package_version, limit: limit).execute }
it { is_expected.to match_array([package1, package2]) }
context 'with lower case package name' do
let(:package_name) { package1.name.downcase }
it { is_expected.to match_array([package1, package2]) }
end
context 'with unknown package name' do
let(:package_name) { 'foobar' }
......@@ -30,5 +38,38 @@ describe Packages::Nuget::PackageFinder do
it { is_expected.to be_empty }
end
context 'with limit hit' do
let_it_be(:package4) { create(:nuget_package, name: package1.name, project: project) }
let_it_be(:package5) { create(:nuget_package, name: package1.name, project: project) }
let_it_be(:package6) { create(:nuget_package, name: package1.name, project: project) }
let(:limit) { 2 }
it { is_expected.to match_array([package5, package6]) }
end
context 'with downcase package name' do
let(:package_name) { package1.name.downcase }
it { is_expected.to match_array([package1, package2]) }
end
context 'with prefix wildcard' do
let(:package_name) { "%#{package1.name[3..-1]}" }
it { is_expected.to match_array([package1, package2]) }
end
context 'with suffix wildcard' do
let(:package_name) { "#{package1.name[0..-3]}%" }
it { is_expected.to match_array([package1, package2]) }
end
context 'with surrounding wildcards' do
let(:package_name) { "%#{package1.name[3..-3]}%" }
it { is_expected.to match_array([package1, package2]) }
end
end
end
......@@ -2,32 +2,52 @@
require 'spec_helper'
describe Packages::PackageFileFinder do
let(:package) { create(:maven_package) }
let(:package_file) { package.package_files.first }
let_it_be(:package) { create(:maven_package) }
let_it_be(:package_file) { package.package_files.first }
let(:package_file_name) { package_file.file_name }
let(:params) { {} }
describe '#execute!' do
it 'returns a package file' do
finder = described_class.new(package, package_file.file_name)
RSpec.shared_examples 'package file finder examples' do
it { is_expected.to eq(package_file) }
context 'with conan_file_type' do
let_it_be(:package) { create(:conan_package) }
# conan packages contain a conanmanifest.txt file for both conan_file_types
let(:package_file_name) { 'conanmanifest.txt' }
let(:params) { { conan_file_type: :recipe_file } }
expect(finder.execute!).to eq(package_file)
it { expect(subject.conan_file_type).to eq('recipe_file') }
end
it 'raises an error' do
finder = described_class.new(package, 'unknown.jpg')
context 'with file_name_like' do
let(:package_file_name) { package_file.file_name.upcase }
let(:params) { { with_file_name_like: true } }
expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound)
it { is_expected.to eq(package_file) }
end
end
context 'with conan_file_type' do
let(:package) { create(:conan_package) }
describe '#execute' do
subject { described_class.new(package, package_file_name, params).execute }
it_behaves_like 'package file finder examples'
context 'with unknown file_name' do
let(:package_file_name) { 'unknown.jpg' }
it { expect(subject).to be_nil }
end
end
describe '#execute!' do
subject { described_class.new(package, package_file_name, params).execute! }
it_behaves_like 'package file finder examples'
it 'returns a package of the correct file_type' do
# conan packages contain a conanmanifest.txt file for both conan_file_types
result = described_class.new(package, 'conanmanifest.txt', conan_file_type: :recipe_file).execute!
context 'with unknown file_name' do
let(:package_file_name) { 'unknown.jpg' }
expect(result.conan_file_type).to eq('recipe_file')
expect(result.conan_file_type).not_to eq('package_file')
end
it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
end
end
{
"type": "object",
"required": ["versions"],
"properties": {
"versions": {
"type": "array",
"items": { "type": "string" }
}
}
}
......@@ -11,6 +11,27 @@ RSpec.describe Packages::PackageFile, type: :model do
it { is_expected.to validate_presence_of(:package) }
end
context 'with package filenames' do
let_it_be(:package_file1) { create(:package_file, :xml, file_name: 'FooBar') }
let_it_be(:package_file2) { create(:package_file, :xml, file_name: 'ThisIsATest') }
describe '.with_file_name' do
let(:filename) { 'FooBar' }
subject { described_class.with_file_name(filename) }
it { is_expected.to match_array([package_file1]) }
end
describe '.with_file_name_like' do
let(:filename) { 'foobar' }
subject { described_class.with_file_name_like(filename) }
it { is_expected.to match_array([package_file1]) }
end
end
it_behaves_like 'UpdateProjectStatistics' do
subject { build(:package_file, :jar, size: 42) }
end
......
......@@ -165,4 +165,50 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to match_array([package3, package2]) }
end
context 'with several packages' do
let_it_be(:package1) { create(:nuget_package, name: 'FooBarish') }
let_it_be(:package2) { create(:npm_package) }
let_it_be(:package3) { create(:npm_package) }
describe '.pluck_names' do
subject { described_class.pluck_names.sort }
it { is_expected.to match_array([package1, package2, package3].map(&:name).sort) }
end
describe '.pluck_versions' do
subject { described_class.pluck_versions.sort }
it { is_expected.to match_array([package1, package2, package3].map(&:version).sort) }
end
describe '.with_name_like' do
subject { described_class.with_name_like(package_name) }
context 'with downcase name' do
let(:package_name) { 'foobarish' }
it { is_expected.to match_array([package1]) }
end
context 'with prefix wildcard' do
let(:package_name) { '%arish' }
it { is_expected.to match_array([package1]) }
end
context 'with suffix wildcard' do
let(:package_name) { 'foo%' }
it { is_expected.to match_array([package1]) }
end
context 'with surrounding wildcards' do
let(:package_name) { '%ooba%' }
it { is_expected.to match_array([package1]) }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Packages::Nuget::PackagesVersionsPresenter do
let_it_be(:packages) { create_list(:nuget_package, 5) }
let_it_be(:presenter) { described_class.new(::Packages::Package.all) }
describe '#versions' do
subject { presenter.versions }
it { is_expected.to match_array(packages.map(&:version).sort) }
end
end
......@@ -230,7 +230,7 @@ describe API::NugetPackages do
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/index' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
let_it_be(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.json" }
subject { get api(url) }
......@@ -293,10 +293,10 @@ describe API::NugetPackages do
it_behaves_like 'rejects nuget packages access with packages features disabled'
end
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/:package_version' do
describe 'GET /api/v4/projects/:id/packages/nuget/metadata/*package_name/*package_version' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:package) { create(:nuget_package, name: 'Dummy.Package', project: project) }
let_it_be(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
subject { get api(url) }
......@@ -351,6 +351,153 @@ describe API::NugetPackages do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
context 'with invalid package name' do
let_it_be(:package_name) { 'Unkown' }
it_behaves_like 'rejects nuget packages access', :developer, :not_found
end
end
it_behaves_like 'rejects nuget packages access with feature flag disabled'
end
it_behaves_like 'rejects nuget packages access with packages features disabled'
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/index' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:packages) { create_list(:nuget_package, 5, name: package_name, project: project) }
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.json" }
subject { get api(url) }
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
end
context 'with feature flag enabled' do
before do
stub_feature_flags(nuget_package_registry: { enabled: true, thing: project })
end
context 'with valid project' do
using RSpec::Parameterized::TableSyntax
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'process nuget download versions request' | :success
'PUBLIC' | :guest | true | true | 'process nuget download versions request' | :success
'PUBLIC' | :developer | true | false | 'process nuget download versions request' | :success
'PUBLIC' | :guest | true | false | 'process nuget download versions request' | :success
'PUBLIC' | :developer | false | true | 'process nuget download versions request' | :success
'PUBLIC' | :guest | false | true | 'process nuget download versions request' | :success
'PUBLIC' | :developer | false | false | 'process nuget download versions request' | :success
'PUBLIC' | :guest | false | false | 'process nuget download versions request' | :success
'PUBLIC' | :anonymous | false | true | 'process nuget download versions request' | :success
'PRIVATE' | :developer | true | true | 'process nuget download versions request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
subject { get api(url), headers: headers }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
after do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
it_behaves_like 'rejects nuget access with unknown project id'
it_behaves_like 'rejects nuget access with invalid project id'
end
it_behaves_like 'rejects nuget packages access with feature flag disabled'
end
it_behaves_like 'rejects nuget packages access with packages features disabled'
end
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
let_it_be(:package_name) { 'Dummy.Package' }
let_it_be(:package) { create(:nuget_package, project: project, name: package_name) }
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.nupkg" }
subject { get api(url) }
context 'with packages features enabled' do
before do
stub_licensed_features(packages: true)
end
context 'with feature flag enabled' do
before do
stub_feature_flags(nuget_package_registry: { enabled: true, thing: project })
end
context 'with valid project' do
using RSpec::Parameterized::TableSyntax
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'process nuget download content request' | :success
'PUBLIC' | :guest | true | true | 'process nuget download content request' | :success
'PUBLIC' | :developer | true | false | 'process nuget download content request' | :success
'PUBLIC' | :guest | true | false | 'process nuget download content request' | :success
'PUBLIC' | :developer | false | true | 'process nuget download content request' | :success
'PUBLIC' | :guest | false | true | 'process nuget download content request' | :success
'PUBLIC' | :developer | false | false | 'process nuget download content request' | :success
'PUBLIC' | :guest | false | false | 'process nuget download content request' | :success
'PUBLIC' | :anonymous | false | true | 'process nuget download content request' | :success
'PRIVATE' | :developer | true | true | 'process nuget download content request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
subject { get api(url), headers: headers }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
after do
project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
it_behaves_like 'rejects nuget access with unknown project id'
it_behaves_like 'rejects nuget access with invalid project id'
end
it_behaves_like 'rejects nuget packages access with feature flag disabled'
......
......@@ -50,13 +50,8 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget service index json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/service_index', dir: 'ee')
expect(json_response).to be_a(Hash)
end
context 'with invalid format' do
......@@ -67,6 +62,16 @@ RSpec.shared_examples 'process nuget service index request' do |user_type, statu
end
end
RSpec.shared_examples 'returning nuget metadata json response with json schema' do |json_schema|
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to match_schema(json_schema, dir: 'ee')
expect(json_response).to be_a(Hash)
end
end
RSpec.shared_examples 'process nuget metadata request at package name level' do |user_type, status, add_member = true|
context "for user type #{user_type}" do
before do
......@@ -75,24 +80,21 @@ RSpec.shared_examples 'process nuget metadata request at package name level' do
it_behaves_like 'returning response status', status
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget packages metadata json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/packages_metadata', dir: 'ee')
end
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/index.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
end
end
end
......@@ -104,24 +106,21 @@ RSpec.shared_examples 'process nuget metadata request at package name and packag
it_behaves_like 'returning response status', status
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to be_a(Hash)
end
it 'returns a valid nuget package metadata json' do
subject
expect(json_response).to match_schema('public_api/v4/packages/nuget/package_metadata', dir: 'ee')
end
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
end
end
end
......@@ -223,6 +222,77 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
end
end
RSpec.shared_examples 'process nuget download versions request' do |user_type, status, add_member = true|
RSpec.shared_examples 'returns a valid nuget download versions json response' do
it 'returns a valid json response' do
subject
expect(response.content_type.to_s).to eq('application/json')
expect(json_response).to match_schema('public_api/v4/packages/nuget/download_versions', dir: 'ee')
expect(json_response).to be_a(Hash)
expect(json_response['versions']).to match_array(packages.map(&:version).sort)
end
end
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it_behaves_like 'returning response status', status
it_behaves_like 'returns a valid nuget download versions json response'
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package_name}/index.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it_behaves_like 'returns a valid nuget download versions json response'
end
end
end
RSpec.shared_examples 'process nuget download content request' do |user_type, status, add_member = true|
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it_behaves_like 'returning response status', status
it 'returns a valid package archive' do
subject
expect(response.content_type.to_s).to eq('application/octet-stream')
end
context 'with invalid format' do
let(:url) { "/projects/#{project.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls" }
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
end
context 'with lower case package name' do
let_it_be(:package_name) { 'dummy.package' }
it_behaves_like 'returning response status', status
it 'returns a valid package archive' do
subject
expect(response.content_type.to_s).to eq('application/octet-stream')
end
end
end
end
RSpec.shared_examples 'rejects nuget access with invalid project id' do
context 'with a project id with invalid integers' do
using RSpec::Parameterized::TableSyntax
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment