diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 21:18:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 21:18:33 +0300 |
commit | f64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch) | |
tree | a2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /spec/services/packages | |
parent | bfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff) |
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'spec/services/packages')
13 files changed, 912 insertions, 82 deletions
diff --git a/spec/services/packages/composer/create_package_service_spec.rb b/spec/services/packages/composer/create_package_service_spec.rb index 4f1a46e7e45..526c7b4929b 100644 --- a/spec/services/packages/composer/create_package_service_spec.rb +++ b/spec/services/packages/composer/create_package_service_spec.rb @@ -28,6 +28,8 @@ RSpec.describe Packages::Composer::CreatePackageService do let(:branch) { project.repository.find_branch('master') } it 'creates the package' do + expect(::Packages::Composer::CacheUpdateWorker).to receive(:perform_async).with(project.id, package_name, nil) + expect { subject } .to change { Packages::Package.composer.count }.by(1) .and change { Packages::Composer::Metadatum.count }.by(1) @@ -54,6 +56,8 @@ RSpec.describe Packages::Composer::CreatePackageService do end it 'creates the package' do + expect(::Packages::Composer::CacheUpdateWorker).to receive(:perform_async).with(project.id, package_name, nil) + expect { subject } .to change { Packages::Package.composer.count }.by(1) .and change { Packages::Composer::Metadatum.count }.by(1) @@ -80,6 +84,8 @@ RSpec.describe Packages::Composer::CreatePackageService do end it 'does not create a new package' do + expect(::Packages::Composer::CacheUpdateWorker).to receive(:perform_async).with(project.id, package_name, nil) + expect { subject } .to change { Packages::Package.composer.count }.by(0) .and change { Packages::Composer::Metadatum.count }.by(0) @@ -101,6 +107,8 @@ RSpec.describe Packages::Composer::CreatePackageService do let!(:other_package) { create(:package, name: package_name, version: 'dev-master', project: other_project) } it 'creates the package' do + expect(::Packages::Composer::CacheUpdateWorker).to receive(:perform_async).with(project.id, package_name, nil) + expect { subject } .to change { Packages::Package.composer.count }.by(1) .and change { Packages::Composer::Metadatum.count }.by(1) diff --git a/spec/services/packages/create_event_service_spec.rb b/spec/services/packages/create_event_service_spec.rb index f7bab0e5a9f..122f1e88ad0 100644 --- a/spec/services/packages/create_event_service_spec.rb +++ b/spec/services/packages/create_event_service_spec.rb @@ -57,18 +57,6 @@ RSpec.describe Packages::CreateEventService do end shared_examples 'redis package unique event creation' do |originator_type, expected_scope| - context 'with feature flag disable' do - before do - stub_feature_flags(collect_package_events_redis: false) - end - - it 'does not track the event' do - expect(::Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) - - subject - end - end - it 'tracks the event' do expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(/package/, values: user.id) @@ -77,18 +65,6 @@ RSpec.describe Packages::CreateEventService do end shared_examples 'redis package count event creation' do |originator_type, expected_scope| - context 'with feature flag disabled' do - before do - stub_feature_flags(collect_package_events_redis: false) - end - - it 'does not track the event' do - expect(::Gitlab::UsageDataCounters::PackageEventCounter).not_to receive(:count) - - subject - end - end - it 'tracks the event' do expect(::Gitlab::UsageDataCounters::PackageEventCounter).to receive(:count).at_least(:once) diff --git a/spec/services/packages/create_temporary_package_service_spec.rb b/spec/services/packages/create_temporary_package_service_spec.rb new file mode 100644 index 00000000000..4b8d37401d8 --- /dev/null +++ b/spec/services/packages/create_temporary_package_service_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::CreateTemporaryPackageService do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:params) { {} } + let_it_be(:package_name) { 'my-package' } + let_it_be(:package_type) { 'rubygems' } + + describe '#execute' do + subject { described_class.new(project, user, params).execute(package_type, name: package_name) } + + let(:package) { Packages::Package.last } + + it 'creates the package', :aggregate_failures do + expect { subject }.to change { Packages::Package.count }.by(1) + + expect(package).to be_valid + expect(package).to be_processing + expect(package.name).to eq(package_name) + expect(package.version).to start_with(described_class::PACKAGE_VERSION) + expect(package.package_type).to eq(package_type) + end + + it 'can create two packages in a row', :aggregate_failures do + expect { subject }.to change { Packages::Package.count }.by(1) + + expect do + described_class.new(project, user, params).execute(package_type, name: package_name) + end.to change { Packages::Package.count }.by(1) + + expect(package).to be_valid + expect(package).to be_processing + expect(package.name).to eq(package_name) + expect(package.version).to start_with(described_class::PACKAGE_VERSION) + expect(package.package_type).to eq(package_type) + end + + it_behaves_like 'assigns the package creator' + it_behaves_like 'assigns build to package' + end +end diff --git a/spec/services/packages/debian/get_or_create_incoming_service_spec.rb b/spec/services/packages/debian/find_or_create_incoming_service_spec.rb index ab99b091246..e1393c774b1 100644 --- a/spec/services/packages/debian/get_or_create_incoming_service_spec.rb +++ b/spec/services/packages/debian/find_or_create_incoming_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Packages::Debian::GetOrCreateIncomingService do +RSpec.describe Packages::Debian::FindOrCreateIncomingService do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } diff --git a/spec/services/packages/debian/find_or_create_package_service_spec.rb b/spec/services/packages/debian/find_or_create_package_service_spec.rb new file mode 100644 index 00000000000..3582b1f1dc3 --- /dev/null +++ b/spec/services/packages/debian/find_or_create_package_service_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Debian::FindOrCreatePackageService do + let_it_be(:distribution) { create(:debian_project_distribution) } + let_it_be(:project) { distribution.project } + let_it_be(:user) { create(:user) } + let(:params) { { name: 'foo', version: '1.0+debian', distribution_name: distribution.codename } } + + subject(:service) { described_class.new(project, user, params) } + + describe '#execute' do + subject { service.execute } + + let(:package) { subject.payload[:package] } + + context 'run once' do + it 'creates a new package', :aggregate_failures do + expect { subject }.to change { ::Packages::Package.count }.by(1) + expect(subject).to be_success + + expect(package).to be_valid + expect(package.project_id).to eq(project.id) + expect(package.creator_id).to eq(user.id) + expect(package.name).to eq('foo') + expect(package.version).to eq('1.0+debian') + expect(package).to be_debian + expect(package.debian_publication.distribution).to eq(distribution) + end + end + + context 'run twice' do + let(:subject2) { service.execute } + + let(:package2) { service.execute.payload[:package] } + + it 'returns the same object' do + expect { subject }.to change { ::Packages::Package.count }.by(1) + expect { package2 }.not_to change { ::Packages::Package.count } + + expect(package2.id).to eq(package.id) + end + end + + context 'with non-existing distribution' do + let(:params) { { name: 'foo', version: '1.0+debian', distribution_name: 'not-existing' } } + + it 'raises ActiveRecord::RecordNotFound' do + expect { package }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/spec/services/packages/maven/metadata/append_package_file_service_spec.rb b/spec/services/packages/maven/metadata/append_package_file_service_spec.rb new file mode 100644 index 00000000000..c406ab93630 --- /dev/null +++ b/spec/services/packages/maven/metadata/append_package_file_service_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::Maven::Metadata::AppendPackageFileService do + let_it_be(:package) { create(:maven_package, version: nil) } + + let(:service) { described_class.new(package: package, metadata_content: content) } + let(:content) { 'test' } + + describe '#execute' do + subject { service.execute } + + context 'with some content' do + it 'creates all the related package files', :aggregate_failures do + expect { subject }.to change { package.package_files.count }.by(5) + expect(subject).to be_success + + expect_file(metadata_file_name, with_content: content, with_content_type: 'application/xml') + expect_file("#{metadata_file_name}.md5") + expect_file("#{metadata_file_name}.sha1") + expect_file("#{metadata_file_name}.sha256") + expect_file("#{metadata_file_name}.sha512") + end + end + + context 'with nil content' do + let(:content) { nil } + + it_behaves_like 'returning an error service response', message: 'metadata content is not set' + end + + context 'with nil package' do + let(:package) { nil } + + it_behaves_like 'returning an error service response', message: 'package is not set' + end + + def expect_file(file_name, with_content: nil, with_content_type: '') + package_file = package.package_files.recent.with_file_name(file_name).first + + expect(package_file.file).to be_present + expect(package_file.file_name).to eq(file_name) + expect(package_file.size).to be > 0 + expect(package_file.file_md5).to be_present + expect(package_file.file_sha1).to be_present + expect(package_file.file_sha256).to be_present + expect(package_file.file.content_type).to eq(with_content_type) + + if with_content + expect(package_file.file.read).to eq(with_content) + end + end + + def metadata_file_name + ::Packages::Maven::Metadata.filename + end + end +end diff --git a/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb b/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb new file mode 100644 index 00000000000..6fc1087940d --- /dev/null +++ b/spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::Maven::Metadata::CreatePluginsXmlService do + let_it_be(:group_id) { 'my/test' } + let_it_be(:package) { create(:maven_package, name: group_id, version: nil) } + + let(:plugins_in_database) { %w[one-maven-plugin two three-maven-plugin] } + let(:plugins_in_xml) { %w[one-maven-plugin two three-maven-plugin] } + let(:service) { described_class.new(metadata_content: metadata_xml, package: package) } + + describe '#execute' do + subject { service.execute } + + before do + next unless package + + plugins_in_database.each do |plugin| + create( + :maven_package, + name: "#{group_id}/#{plugin}", + version: '1.0.0', + project: package.project, + maven_metadatum_attributes: { + app_group: group_id.tr('/', '.'), + app_name: plugin, + app_version: '1.0.0' + } + ) + end + end + + shared_examples 'returning an xml with plugins from the database' do + it 'returns an metadata versions xml with versions in the database', :aggregate_failures do + expect(subject).to be_success + expect(subject.payload[:changes_exist]).to eq(true) + expect(subject.payload[:empty_versions]).to eq(false) + expect(plugins_from(subject.payload[:metadata_content])).to match_array(plugins_in_database) + end + end + + shared_examples 'returning no changes' do + it 'returns no changes', :aggregate_failures do + expect(subject).to be_success + expect(subject.payload).to eq(changes_exist: false, empty_versions: false) + end + end + + context 'with same plugins on both sides' do + it_behaves_like 'returning no changes' + end + + context 'with more plugins' do + let(:additional_plugins) { %w[four-maven-plugin five] } + + context 'in database' do + let(:plugins_in_database) { plugins_in_xml + additional_plugins } + + # we can't distinguish that the additional plugin are actually maven plugins + it_behaves_like 'returning no changes' + end + + context 'in xml' do + let(:plugins_in_xml) { plugins_in_database + additional_plugins } + + it_behaves_like 'returning an xml with plugins from the database' + end + end + + context 'with no versions in the database' do + let(:plugins_in_database) { [] } + + it 'returns a success', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(result.payload).to eq(changes_exist: true, empty_plugins: true) + end + end + + context 'with an incomplete metadata content' do + let(:metadata_xml) { '<metadata></metadata>' } + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + context 'with an invalid metadata content' do + let(:metadata_xml) { '<meta></metadata>' } + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + it_behaves_like 'handling metadata content pointing to a file for the create xml service' + + it_behaves_like 'handling invalid parameters for create xml service' + end + + def metadata_xml + Nokogiri::XML::Builder.new do |xml| + xml.metadata do + xml.plugins do + plugins_in_xml.each do |plugin| + xml.plugin do + xml.name(plugin) + xml.prefix(prefix_from(plugin)) + xml.artifactId(plugin) + end + end + end + end + end.to_xml + end + + def prefix_from(artifact_id) + artifact_id.gsub(/-?maven-?/, '') + .gsub(/-?plugin-?/, '') + end + + def plugins_from(xml_content) + doc = Nokogiri::XML(xml_content) + doc.xpath('//metadata/plugins/plugin/artifactId').map(&:content) + end +end diff --git a/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb b/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb new file mode 100644 index 00000000000..39c6feb5d12 --- /dev/null +++ b/spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::Maven::Metadata::CreateVersionsXmlService do + let_it_be(:package) { create(:maven_package, version: nil) } + + let(:versions_in_database) { %w[1.3 2.0-SNAPSHOT 1.6 1.4 1.5-SNAPSHOT] } + let(:versions_in_xml) { %w[1.3 2.0-SNAPSHOT 1.6 1.4 1.5-SNAPSHOT] } + let(:version_latest) { nil } + let(:version_release) { '1.4' } + let(:service) { described_class.new(metadata_content: metadata_xml, package: package) } + + describe '#execute' do + subject { service.execute } + + before do + next unless package + + versions_in_database.each do |version| + create(:maven_package, name: package.name, version: version, project: package.project) + end + end + + shared_examples 'returning an xml with versions in the database' do + it 'returns an metadata versions xml with versions in the database', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(versions_from(result.payload[:metadata_content])).to match_array(versions_in_database) + end + end + + shared_examples 'returning an xml with' do |release:, latest:| + it 'returns an xml with the updated release and latest versions', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(result.payload[:changes_exist]).to be_truthy + xml = result.payload[:metadata_content] + expect(release_from(xml)).to eq(release) + expect(latest_from(xml)).to eq(latest) + end + end + + context 'with same versions in both sides' do + it 'returns no changes', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(result.payload).to eq(changes_exist: false, empty_versions: false) + end + end + + context 'with more versions' do + let(:additional_versions) { %w[5.5 5.6 5.7-SNAPSHOT] } + + context 'in the xml side' do + let(:versions_in_xml) { versions_in_database + additional_versions } + + it_behaves_like 'returning an xml with versions in the database' + end + + context 'in the database side' do + let(:versions_in_database) { versions_in_xml + additional_versions } + + it_behaves_like 'returning an xml with versions in the database' + end + end + + context 'with completely different versions' do + let(:versions_in_database) { %w[1.0 1.1 1.2] } + let(:versions_in_xml) { %w[2.0 2.1 2.2] } + + it_behaves_like 'returning an xml with versions in the database' + end + + context 'with no versions in the database' do + let(:versions_in_database) { [] } + + it 'returns a success', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(result.payload).to eq(changes_exist: true, empty_versions: true) + end + + context 'with an xml without a release version' do + let(:version_release) { nil } + + it 'returns a success', :aggregate_failures do + result = subject + + expect(result).to be_success + expect(result.payload).to eq(changes_exist: true, empty_versions: true) + end + end + end + + context 'with differences in both sides' do + let(:shared_versions) { %w[1.3 2.0-SNAPSHOT 1.6 1.4 1.5-SNAPSHOT] } + let(:additional_versions_in_xml) { %w[5.5 5.6 5.7-SNAPSHOT] } + let(:versions_in_xml) { shared_versions + additional_versions_in_xml } + let(:additional_versions_in_database) { %w[6.5 6.6 6.7-SNAPSHOT] } + let(:versions_in_database) { shared_versions + additional_versions_in_database } + + it_behaves_like 'returning an xml with versions in the database' + end + + context 'with a new release and latest from the database' do + let(:versions_in_database) { versions_in_xml + %w[4.1 4.2-SNAPSHOT] } + + it_behaves_like 'returning an xml with', release: '4.1', latest: nil + + context 'with a latest in the xml' do + let(:version_latest) { '1.6' } + + it_behaves_like 'returning an xml with', release: '4.1', latest: '4.2-SNAPSHOT' + end + end + + context 'with release and latest not existing in the database' do + let(:version_release) { '7.0' } + let(:version_latest) { '8.0-SNAPSHOT' } + + it_behaves_like 'returning an xml with', release: '1.4', latest: '1.5-SNAPSHOT' + end + + context 'with added versions in the database side no more recent than release' do + let(:versions_in_database) { versions_in_xml + %w[4.1 4.2-SNAPSHOT] } + + before do + ::Packages::Package.find_by(name: package.name, version: '4.1').update!(created_at: 2.weeks.ago) + ::Packages::Package.find_by(name: package.name, version: '4.2-SNAPSHOT').update!(created_at: 2.weeks.ago) + end + + it_behaves_like 'returning an xml with', release: '1.4', latest: nil + + context 'with a latest in the xml' do + let(:version_latest) { '1.6' } + + it_behaves_like 'returning an xml with', release: '1.4', latest: '1.5-SNAPSHOT' + end + end + + context 'only snapshot versions are in the database' do + let(:versions_in_database) { %w[4.2-SNAPSHOT] } + + it_behaves_like 'returning an xml with', release: nil, latest: nil + + it 'returns an xml without any release element' do + result = subject + + xml_doc = Nokogiri::XML(result.payload[:metadata_content]) + expect(xml_doc.xpath('//metadata/versioning/release')).to be_empty + end + end + + context 'last updated timestamp' do + let(:versions_in_database) { versions_in_xml + %w[4.1 4.2-SNAPSHOT] } + + it 'updates the last updated timestamp' do + original = last_updated_from(metadata_xml) + + result = subject + + expect(result).to be_success + expect(original).not_to eq(last_updated_from(result.payload[:metadata_content])) + end + end + + context 'with an incomplete metadata content' do + let(:metadata_xml) { '<metadata></metadata>' } + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + context 'with an invalid metadata content' do + let(:metadata_xml) { '<meta></metadata>' } + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + it_behaves_like 'handling metadata content pointing to a file for the create xml service' + + it_behaves_like 'handling invalid parameters for create xml service' + end + + def metadata_xml + Nokogiri::XML::Builder.new do |xml| + xml.metadata do + xml.groupId(package.maven_metadatum.app_group) + xml.artifactId(package.maven_metadatum.app_name) + xml.versioning do + xml.release(version_release) if version_release + xml.latest(version_latest) if version_latest + xml.lastUpdated('20210113130531') + xml.versions do + versions_in_xml.each do |version| + xml.version(version) + end + end + end + end + end.to_xml + end + + def versions_from(xml_content) + doc = Nokogiri::XML(xml_content) + doc.xpath('//metadata/versioning/versions/version').map(&:content) + end + + def release_from(xml_content) + doc = Nokogiri::XML(xml_content) + doc.xpath('//metadata/versioning/release').first&.content + end + + def latest_from(xml_content) + doc = Nokogiri::XML(xml_content) + doc.xpath('//metadata/versioning/latest').first&.content + end + + def last_updated_from(xml_content) + doc = Nokogiri::XML(xml_content) + doc.xpath('//metadata/versioning/lastUpdated').first.content + end +end diff --git a/spec/services/packages/maven/metadata/sync_service_spec.rb b/spec/services/packages/maven/metadata/sync_service_spec.rb new file mode 100644 index 00000000000..f5634159e6d --- /dev/null +++ b/spec/services/packages/maven/metadata/sync_service_spec.rb @@ -0,0 +1,259 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Packages::Maven::Metadata::SyncService do + using RSpec::Parameterized::TableSyntax + + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be_with_reload(:versionless_package_for_versions) { create(:maven_package, name: 'test', version: nil, project: project) } + let_it_be_with_reload(:metadata_file_for_versions) { create(:package_file, :xml, package: versionless_package_for_versions) } + + let(:service) { described_class.new(container: project, current_user: user, params: { package_name: versionless_package_for_versions.name }) } + + describe '#execute' do + let(:create_versions_xml_service_double) { double(::Packages::Maven::Metadata::CreateVersionsXmlService, execute: create_versions_xml_service_response) } + let(:append_package_file_service_double) { double(::Packages::Maven::Metadata::AppendPackageFileService, execute: append_package_file_service_response) } + + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_versions: false, metadata_content: 'test' }) } + let(:append_package_file_service_response) { ServiceResponse.success(message: 'New metadata package files created') } + + subject { service.execute } + + before do + allow(::Packages::Maven::Metadata::CreateVersionsXmlService) + .to receive(:new).with(metadata_content: an_instance_of(ObjectStorage::Concern::OpenFile), package: versionless_package_for_versions).and_return(create_versions_xml_service_double) + allow(::Packages::Maven::Metadata::AppendPackageFileService) + .to receive(:new).with(metadata_content: an_instance_of(String), package: versionless_package_for_versions).and_return(append_package_file_service_double) + end + + context 'permissions' do + where(:role, :expected_result) do + :anonymous | :rejected + :developer | :rejected + :maintainer | :accepted + end + + with_them do + if params[:role] == :anonymous + let_it_be(:user) { nil } + end + + before do + project.send("add_#{role}", user) unless role == :anonymous + end + + if params[:expected_result] == :rejected + it_behaves_like 'returning an error service response', message: 'Not allowed' + else + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + end + end + end + + context 'with a maintainer' do + before do + project.add_maintainer(user) + end + + context 'with a jar package' do + before do + expect(::Packages::Maven::Metadata::CreatePluginsXmlService).not_to receive(:new) + end + + context 'with no changes' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: false }) } + + before do + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'No changes for versions xml' + end + + context 'with changes' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_versions: false, metadata_content: 'new metadata' }) } + + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + + context 'with empty versions' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_versions: true }) } + + before do + expect(service.send(:versionless_package_for_versions)).to receive(:destroy!) + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'Versionless package for versions destroyed' + end + end + + context 'with a too big maven metadata file for versions' do + before do + metadata_file_for_versions.update!(size: 100.megabytes) + end + + it_behaves_like 'returning an error service response', message: 'Metadata file for versions is too big' + end + + context 'an error from the create versions xml service' do + let(:create_versions_xml_service_response) { ServiceResponse.error(message: 'metadata_content is invalid') } + + before do + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + context 'an error from the append package file service' do + let(:append_package_file_service_response) { ServiceResponse.error(message: 'metadata content is not set') } + + it_behaves_like 'returning an error service response', message: 'metadata content is not set' + end + + context 'without a package name' do + let(:service) { described_class.new(container: project, current_user: user, params: { package_name: nil }) } + + before do + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'Blank package name' + end + + context 'without a versionless package for version' do + before do + versionless_package_for_versions.update!(version: '2.2.2') + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'Non existing versionless package' + end + + context 'without a metadata package file for versions' do + before do + versionless_package_for_versions.package_files.update_all(file_name: 'test.txt') + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'Non existing metadata file for versions' + end + + context 'without a project' do + let(:service) { described_class.new(container: nil, current_user: user, params: { package_name: versionless_package_for_versions.name }) } + + before do + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'Not allowed' + end + end + + context 'with a maven plugin package' do + let_it_be(:versionless_package_name_for_plugins) { versionless_package_for_versions.maven_metadatum.app_group.tr('.', '/') } + let_it_be_with_reload(:versionless_package_for_plugins) { create(:maven_package, name: versionless_package_name_for_plugins, version: nil, project: project) } + let_it_be_with_reload(:metadata_file_for_plugins) { create(:package_file, :xml, package: versionless_package_for_plugins) } + + let(:create_plugins_xml_service_double) { double(::Packages::Maven::Metadata::CreatePluginsXmlService, execute: create_plugins_xml_service_response) } + let(:create_plugins_xml_service_response) { ServiceResponse.success(payload: { changes_exist: false }) } + + before do + allow(::Packages::Maven::Metadata::CreatePluginsXmlService) + .to receive(:new).with(metadata_content: an_instance_of(ObjectStorage::Concern::OpenFile), package: versionless_package_for_plugins).and_return(create_plugins_xml_service_double) + allow(::Packages::Maven::Metadata::AppendPackageFileService) + .to receive(:new).with(metadata_content: an_instance_of(String), package: versionless_package_for_plugins).and_return(append_package_file_service_double) + end + + context 'with no changes' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: false }) } + + before do + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'No changes for versions xml' + end + + context 'with changes in the versions xml' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_versions: false, metadata_content: 'new metadata' }) } + + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + + context 'with changes in the plugin xml' do + let(:create_plugins_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_plugins: false, metadata_content: 'new metadata' }) } + + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + end + + context 'with empty versions' do + let(:create_versions_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_versions: true }) } + let(:create_plugins_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_plugins: true }) } + + before do + expect(service.send(:versionless_package_for_versions)).to receive(:destroy!) + expect(service.send(:metadata_package_file_for_plugins).package).to receive(:destroy!) + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'Versionless package for versions destroyed' + end + + context 'with a too big maven metadata file for versions' do + before do + metadata_file_for_plugins.update!(size: 100.megabytes) + end + + it_behaves_like 'returning an error service response', message: 'Metadata file for plugins is too big' + end + + context 'an error from the create versions xml service' do + let(:create_plugins_xml_service_response) { ServiceResponse.error(message: 'metadata_content is invalid') } + + before do + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + expect(::Packages::Maven::Metadata::AppendPackageFileService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'metadata_content is invalid' + end + + context 'an error from the append package file service' do + let(:create_plugins_xml_service_response) { ServiceResponse.success(payload: { changes_exist: true, empty_plugins: false, metadata_content: 'new metadata' }) } + let(:append_package_file_service_response) { ServiceResponse.error(message: 'metadata content is not set') } + + before do + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning an error service response', message: 'metadata content is not set' + end + + context 'without a versionless package for plugins' do + before do + versionless_package_for_plugins.package_files.update_all(file_name: 'test.txt') + expect(::Packages::Maven::Metadata::CreatePluginsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + end + + context 'without a metadata package file for plugins' do + before do + versionless_package_for_plugins.package_files.update_all(file_name: 'test.txt') + expect(::Packages::Maven::Metadata::CreatePluginsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'New metadata package files created' + end + end + end + end + end +end diff --git a/spec/services/packages/npm/create_package_service_spec.rb b/spec/services/packages/npm/create_package_service_spec.rb index 10fce6c1651..ba5729eaf59 100644 --- a/spec/services/packages/npm/create_package_service_spec.rb +++ b/spec/services/packages/npm/create_package_service_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Packages::Npm::CreatePackageService do end let(:override) { {} } - let(:package_name) { "@#{namespace.path}/my-app".freeze } + let(:package_name) { "@#{namespace.path}/my-app" } subject { described_class.new(project, user, params).execute } @@ -42,29 +42,35 @@ RSpec.describe Packages::Npm::CreatePackageService do it { expect(subject.name).to eq(package_name) } it { expect(subject.version).to eq(version) } + + context 'with build info' do + let(:job) { create(:ci_build, user: user) } + let(:params) { super().merge(build: job) } + + it_behaves_like 'assigns build to package' + it_behaves_like 'assigns status to package' + + it 'creates a package file build info' do + expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1) + end + end end describe '#execute' do context 'scoped package' do it_behaves_like 'valid package' + end - context 'with build info' do - let(:job) { create(:ci_build, user: user) } - let(:params) { super().merge(build: job) } - - it_behaves_like 'assigns build to package' - it_behaves_like 'assigns status to package' + context 'scoped package not following the naming convention' do + let(:package_name) { '@any-scope/package' } - it 'creates a package file build info' do - expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1) - end - end + it_behaves_like 'valid package' end - context 'invalid package name' do - let(:package_name) { "@#{namespace.path}/my-group/my-app".freeze } + context 'unscoped package' do + let(:package_name) { 'unscoped-package' } - it { expect { subject }.to raise_error(ActiveRecord::RecordInvalid) } + it_behaves_like 'valid package' end context 'package already exists' do @@ -84,11 +90,18 @@ RSpec.describe Packages::Npm::CreatePackageService do it { expect(subject[:message]).to be 'File is too large.' } end - context 'with incorrect namespace' do - let(:package_name) { '@my_other_namespace/my-app' } - - it 'raises a RecordInvalid error' do - expect { subject }.to raise_error(ActiveRecord::RecordInvalid) + [ + '@inv@lid_scope/package', + '@scope/sub/group', + '@scope/../../package', + '@scope%2e%2e%2fpackage' + ].each do |invalid_package_name| + context "with invalid name #{invalid_package_name}" do + let(:package_name) { invalid_package_name } + + it 'raises a RecordInvalid error' do + expect { subject }.to raise_error(ActiveRecord::RecordInvalid) + end end end diff --git a/spec/services/packages/nuget/create_package_service_spec.rb b/spec/services/packages/nuget/create_package_service_spec.rb deleted file mode 100644 index e338ac36fc3..00000000000 --- a/spec/services/packages/nuget/create_package_service_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -require 'spec_helper' - -RSpec.describe Packages::Nuget::CreatePackageService do - let_it_be(:project) { create(:project) } - let_it_be(:user) { create(:user) } - let_it_be(:params) { {} } - - describe '#execute' do - subject { described_class.new(project, user, params).execute } - - let(:package) { Packages::Package.last } - - it 'creates the package' do - expect { subject }.to change { Packages::Package.count }.by(1) - - expect(package).to be_valid - expect(package.name).to eq(Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) - expect(package.version).to start_with(Packages::Nuget::CreatePackageService::PACKAGE_VERSION) - expect(package.package_type).to eq('nuget') - end - - it 'can create two packages in a row' do - expect { subject }.to change { Packages::Package.count }.by(1) - expect { described_class.new(project, user, params).execute }.to change { Packages::Package.count }.by(1) - - expect(package).to be_valid - expect(package.name).to eq(Packages::Nuget::CreatePackageService::TEMPORARY_PACKAGE_NAME) - expect(package.version).to start_with(Packages::Nuget::CreatePackageService::PACKAGE_VERSION) - expect(package.package_type).to eq('nuget') - end - - it_behaves_like 'assigns the package creator' - it_behaves_like 'assigns build to package' - it_behaves_like 'assigns status to package' - end -end diff --git a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb index 92b493ed376..c1cce46a54c 100644 --- a/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb +++ b/spec/services/packages/nuget/update_package_from_metadata_service_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_redis_shared_state do include ExclusiveLeaseHelpers - let(:package) { create(:nuget_package) } + let(:package) { create(:nuget_package, :processing) } let(:package_file) { package.package_files.first } let(:service) { described_class.new(package_file) } let(:package_name) { 'DummyProject.DummyPackage' } @@ -60,6 +60,7 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_ .to change { ::Packages::Package.count }.by(0) .and change { Packages::DependencyLink.count }.by(0) expect(package_file.reload.file_name).not_to eq(package_file_name) + expect(package_file.package).to be_processing expect(package_file.package.reload.name).not_to eq(package_name) expect(package_file.package.version).not_to eq(package_version) end @@ -78,6 +79,7 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_ expect(package.reload.name).to eq(package_name) expect(package.version).to eq(package_version) + expect(package).to be_default expect(package_file.reload.file_name).to eq(package_file_name) # hard reset needed to properly reload package_file.file expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 @@ -184,6 +186,7 @@ RSpec.describe Packages::Nuget::UpdatePackageFromMetadataService, :clean_gitlab_ expect(package.reload.name).to eq(package_name) expect(package.version).to eq(package_version) + expect(package).to be_default expect(package_file.reload.file_name).to eq(package_file_name) # hard reset needed to properly reload package_file.file expect(Packages::PackageFile.find(package_file.id).file.size).not_to eq 0 diff --git a/spec/services/packages/rubygems/dependency_resolver_service_spec.rb b/spec/services/packages/rubygems/dependency_resolver_service_spec.rb new file mode 100644 index 00000000000..206bffe53f8 --- /dev/null +++ b/spec/services/packages/rubygems/dependency_resolver_service_spec.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Rubygems::DependencyResolverService do + let_it_be(:project) { create(:project, :private) } + let_it_be(:package) { create(:package, project: project) } + let_it_be(:user) { create(:user) } + let(:gem_name) { package.name } + let(:service) { described_class.new(project, user, gem_name: gem_name) } + + describe '#execute' do + subject { service.execute } + + context 'user without access' do + it 'returns a service error' do + expect(subject.error?).to be(true) + expect(subject.message).to eq('forbidden') + end + end + + context 'user with access' do + before do + project.add_developer(user) + end + + context 'when no package is found' do + let(:gem_name) { nil } + + it 'returns a service error', :aggregate_failures do + expect(subject.error?).to be(true) + expect(subject.message).to eq("#{gem_name} not found") + end + end + + context 'package without dependencies' do + it 'returns an empty dependencies array' do + expected_result = [{ + name: package.name, + number: package.version, + platform: described_class::DEFAULT_PLATFORM, + dependencies: [] + }] + + expect(subject.payload).to eq(expected_result) + end + end + + context 'package with dependencies' do + let(:dependency_link) { create(:packages_dependency_link, :rubygems, package: package)} + let(:dependency_link2) { create(:packages_dependency_link, :rubygems, package: package)} + let(:dependency_link3) { create(:packages_dependency_link, :rubygems, package: package)} + + it 'returns a set of dependencies' do + expected_result = [{ + name: package.name, + number: package.version, + platform: described_class::DEFAULT_PLATFORM, + dependencies: [ + [dependency_link.dependency.name, dependency_link.dependency.version_pattern], + [dependency_link2.dependency.name, dependency_link2.dependency.version_pattern], + [dependency_link3.dependency.name, dependency_link3.dependency.version_pattern] + ] + }] + + expect(subject.payload).to eq(expected_result) + end + end + + context 'package with multiple versions' do + let(:dependency_link) { create(:packages_dependency_link, :rubygems, package: package)} + let(:dependency_link2) { create(:packages_dependency_link, :rubygems, package: package)} + let(:dependency_link3) { create(:packages_dependency_link, :rubygems, package: package)} + let(:package2) { create(:package, project: project, name: package.name, version: '9.9.9') } + let(:dependency_link4) { create(:packages_dependency_link, :rubygems, package: package2)} + + it 'returns a set of dependencies' do + expected_result = [{ + name: package.name, + number: package.version, + platform: described_class::DEFAULT_PLATFORM, + dependencies: [ + [dependency_link.dependency.name, dependency_link.dependency.version_pattern], + [dependency_link2.dependency.name, dependency_link2.dependency.version_pattern], + [dependency_link3.dependency.name, dependency_link3.dependency.version_pattern] + ] + }, { + name: package2.name, + number: package2.version, + platform: described_class::DEFAULT_PLATFORM, + dependencies: [ + [dependency_link4.dependency.name, dependency_link4.dependency.version_pattern] + ] + }] + + expect(subject.payload).to eq(expected_result) + end + end + end + end +end |