diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-16 18:18:33 +0000 |
commit | f64a639bcfa1fc2bc89ca7db268f594306edfd7c (patch) | |
tree | a2c3c2ebcc3b45e596949db485d6ed18ffaacfa1 /spec/services/packages/maven | |
parent | bfbc3e0d6583ea1a91f627528bedc3d65ba4b10f (diff) | |
download | gitlab-ce-f64a639bcfa1fc2bc89ca7db268f594306edfd7c.tar.gz |
Add latest changes from gitlab-org/gitlab@13-10-stable-eev13.10.0-rc40
Diffstat (limited to 'spec/services/packages/maven')
4 files changed, 669 insertions, 0 deletions
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 |