diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-21 02:50:22 +0300 |
commit | 9dc93a4519d9d5d7be48ff274127136236a3adb3 (patch) | |
tree | 70467ae3692a0e35e5ea56bcb803eb512a10bedb /spec/services/packages | |
parent | 4b0f34b6d759d6299322b3a54453e930c6121ff0 (diff) |
Add latest changes from gitlab-org/gitlab@13-11-stable-eev13.11.0-rc43
Diffstat (limited to 'spec/services/packages')
13 files changed, 642 insertions, 57 deletions
diff --git a/spec/services/packages/create_dependency_service_spec.rb b/spec/services/packages/create_dependency_service_spec.rb index 3ad59b31b2c..3eae9f099f7 100644 --- a/spec/services/packages/create_dependency_service_spec.rb +++ b/spec/services/packages/create_dependency_service_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Packages::CreateDependencyService do describe '#execute' do let_it_be(:namespace) {create(:namespace)} let_it_be(:version) { '1.0.1' } - let_it_be(:package_name) { "@#{namespace.path}/my-app".freeze } + let_it_be(:package_name) { "@#{namespace.path}/my-app" } context 'when packages are published' do let(:json_file) { 'packages/npm/payload.json' } diff --git a/spec/services/packages/debian/extract_changes_metadata_service_spec.rb b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb new file mode 100644 index 00000000000..2a92b8ed26e --- /dev/null +++ b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb @@ -0,0 +1,160 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Debian::ExtractChangesMetadataService do + describe '#execute' do + let_it_be(:distribution) { create(:debian_project_distribution, codename: 'unstable') } + let_it_be(:incoming) { create(:debian_incoming, project: distribution.project) } + + let(:package_file) { incoming.package_files.last } + let(:service) { described_class.new(package_file) } + + subject { service.execute } + + context 'with valid package file' do + it 'extract metadata', :aggregate_failures do + expected_fields = { 'Architecture' => 'source amd64', 'Binary' => 'libsample0 sample-dev sample-udeb' } + + expect(subject[:file_type]).to eq(:changes) + expect(subject[:architecture]).to be_nil + expect(subject[:fields]).to include(expected_fields) + expect(subject[:files].count).to eq(6) + end + end + + context 'with invalid package file' do + let(:package_file) { incoming.package_files.first } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "is not a changes file") + end + end + + context 'with invalid metadata' do + let(:md5_dsc) { '3b0817804f669e16cdefac583ad88f0e 671 libs optional sample_1.2.3~alpha2.dsc' } + let(:md5_source) { 'd79b34f58f61ff4ad696d9bd0b8daa68 864 libs optional sample_1.2.3~alpha2.tar.xz' } + let(:md5s) { "#{md5_dsc}\n#{md5_source}" } + let(:sha1_dsc) { '32ecbd674f0bfd310df68484d87752490685a8d6 671 sample_1.2.3~alpha2.dsc' } + let(:sha1_source) { '5f8bba5574eb01ac3b1f5e2988e8c29307788236 864 sample_1.2.3~alpha2.tar.xz' } + let(:sha1s) { "#{sha1_dsc}\n#{sha1_source}" } + let(:sha256_dsc) { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba 671 sample_1.2.3~alpha2.dsc' } + let(:sha256_source) { 'b5a599e88e7cbdda3bde808160a21ba1dd1ec76b2ec8d4912aae769648d68362 864 sample_1.2.3~alpha2.tar.xz' } + let(:sha256s) { "#{sha256_dsc}\n#{sha256_source}" } + let(:fields) { { 'Files' => md5s, 'Checksums-Sha1' => sha1s, 'Checksums-Sha256' => sha256s } } + let(:metadata) { { file_type: :changes, architecture: 'amd64', fields: fields } } + + before do + allow_next_instance_of(::Packages::Debian::ExtractMetadataService) do |extract_metadata_service| + allow(extract_metadata_service).to receive(:execute).and_return(metadata) + end + end + + context 'without Files field' do + let(:md5s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Files field is missing") + end + end + + context 'without Checksums-Sha1 field' do + let(:sha1s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Checksums-Sha1 field is missing") + end + end + + context 'without Checksums-Sha256 field' do + let(:sha256s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Checksums-Sha256 field is missing") + end + end + + context 'with file in Checksums-Sha1 but not in Files' do + let(:md5_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.dsc is listed in Checksums-Sha1 but not in Files") + end + end + + context 'with different size in Checksums-Sha1' do + let(:sha1_dsc) { '32ecbd674f0bfd310df68484d87752490685a8d6 42 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Size for sample_1.2.3~alpha2.dsc in Files and Checksums-Sha1 differ") + end + end + + context 'with file in Checksums-Sha256 but not in Files' do + let(:md5_dsc) { '' } + let(:sha1_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.dsc is listed in Checksums-Sha256 but not in Files") + end + end + + context 'with different size in Checksums-Sha256' do + let(:sha256_dsc) { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba 42 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Size for sample_1.2.3~alpha2.dsc in Files and Checksums-Sha256 differ") + end + end + + context 'with file in Files but not in Checksums-Sha1' do + let(:sha1_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha1sum can't be blank") + end + end + + context 'with file in Files but not in Checksums-Sha256' do + let(:sha256_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha256sum can't be blank") + end + end + + context 'with invalid MD5' do + let(:md5_dsc) { '1234567890123456789012345678012 671 libs optional sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Md5sum mismatch for sample_1.2.3~alpha2.dsc: 3b0817804f669e16cdefac583ad88f0e != 1234567890123456789012345678012") + end + end + + context 'with invalid SHA1' do + let(:sha1_dsc) { '1234567890123456789012345678901234567890 671 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha1sum mismatch for sample_1.2.3~alpha2.dsc: 32ecbd674f0bfd310df68484d87752490685a8d6 != 1234567890123456789012345678901234567890") + end + end + + context 'with invalid SHA256' do + let(:sha256_dsc) { '1234567890123456789012345678901234567890123456789012345678901234 671 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha256sum mismatch for sample_1.2.3~alpha2.dsc: 844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba != 1234567890123456789012345678901234567890123456789012345678901234") + end + end + end + + context 'with missing package file' do + before do + incoming.package_files.first.destroy! + end + + it 'raise ArgumentError' do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.tar.xz is listed in Files but was not uploaded") + end + end + end +end diff --git a/spec/services/packages/debian/extract_deb_metadata_service_spec.rb b/spec/services/packages/debian/extract_deb_metadata_service_spec.rb index 33059adf8a2..ee3f3d179dc 100644 --- a/spec/services/packages/debian/extract_deb_metadata_service_spec.rb +++ b/spec/services/packages/debian/extract_deb_metadata_service_spec.rb @@ -10,17 +10,17 @@ RSpec.describe Packages::Debian::ExtractDebMetadataService do context 'with correct file' do it 'return as expected' do expected = { - 'Package': 'libsample0', - 'Source': 'sample', - 'Version': '1.2.3~alpha2', - 'Architecture': 'amd64', - 'Maintainer': 'John Doe <john.doe@example.com>', - 'Installed-Size': '7', - 'Section': 'libs', - 'Priority': 'optional', - 'Multi-Arch': 'same', - 'Homepage': 'https://gitlab.com/', - 'Description': "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." + 'Package' => 'libsample0', + 'Source' => 'sample', + 'Version' => '1.2.3~alpha2', + 'Architecture' => 'amd64', + 'Maintainer' => 'John Doe <john.doe@example.com>', + 'Installed-Size' => '7', + 'Section' => 'libs', + 'Priority' => 'optional', + 'Multi-Arch' => 'same', + 'Homepage' => 'https://gitlab.com/', + 'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." } expect(subject.execute).to eq expected diff --git a/spec/services/packages/debian/extract_metadata_service_spec.rb b/spec/services/packages/debian/extract_metadata_service_spec.rb index 0aa9a67b263..e3911dbbfe0 100644 --- a/spec/services/packages/debian/extract_metadata_service_spec.rb +++ b/spec/services/packages/debian/extract_metadata_service_spec.rb @@ -33,11 +33,11 @@ RSpec.describe Packages::Debian::ExtractMetadataService do where(:case_name, :trait, :expected_file_type, :expected_architecture, :expected_fields) do 'with invalid' | :invalid | :unknown | nil | nil 'with source' | :source | :source | nil | nil - 'with dsc' | :dsc | :dsc | nil | { 'Binary': 'sample-dev, libsample0, sample-udeb' } - 'with deb' | :deb | :deb | 'amd64' | { 'Multi-Arch': 'same' } - 'with udeb' | :udeb | :udeb | 'amd64' | { 'Package': 'sample-udeb' } - 'with buildinfo' | :buildinfo | :buildinfo | nil | { 'Architecture': 'amd64 source', 'Build-Architecture': 'amd64' } - 'with changes' | :changes | :changes | nil | { 'Architecture': 'source amd64', 'Binary': 'libsample0 sample-dev sample-udeb' } + 'with dsc' | :dsc | :dsc | nil | { 'Binary' => 'sample-dev, libsample0, sample-udeb' } + 'with deb' | :deb | :deb | 'amd64' | { 'Multi-Arch' => 'same' } + 'with udeb' | :udeb | :udeb | 'amd64' | { 'Package' => 'sample-udeb' } + 'with buildinfo' | :buildinfo | :buildinfo | nil | { 'Architecture' => 'amd64 source', 'Build-Architecture' => 'amd64' } + 'with changes' | :changes | :changes | nil | { 'Architecture' => 'source amd64', 'Binary' => 'libsample0 sample-dev sample-udeb' } end with_them do diff --git a/spec/services/packages/debian/parse_debian822_service_spec.rb b/spec/services/packages/debian/parse_debian822_service_spec.rb index b67daca89c4..f43e38991ce 100644 --- a/spec/services/packages/debian/parse_debian822_service_spec.rb +++ b/spec/services/packages/debian/parse_debian822_service_spec.rb @@ -27,17 +27,17 @@ RSpec.describe Packages::Debian::ParseDebian822Service do it 'return as expected, preserving order' do expected = { 'Package: libsample0' => { - 'Package': 'libsample0', - 'Source': 'sample', - 'Version': '1.2.3~alpha2', - 'Architecture': 'amd64', - 'Maintainer': 'John Doe <john.doe@example.com>', - 'Installed-Size': '9', - 'Section': 'libs', - 'Priority': 'optional', - 'Multi-Arch': 'same', - 'Homepage': 'https://gitlab.com/', - 'Description': "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." + 'Package' => 'libsample0', + 'Source' => 'sample', + 'Version' => '1.2.3~alpha2', + 'Architecture' => 'amd64', + 'Maintainer' => 'John Doe <john.doe@example.com>', + 'Installed-Size' => '9', + 'Section' => 'libs', + 'Priority' => 'optional', + 'Multi-Arch' => 'same', + 'Homepage' => 'https://gitlab.com/', + 'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." } } @@ -51,38 +51,38 @@ RSpec.describe Packages::Debian::ParseDebian822Service do it 'return as expected, preserving order' do expected = { 'Source: sample' => { - 'Source': 'sample', - 'Priority': 'optional', - 'Maintainer': 'John Doe <john.doe@example.com>', - 'Build-Depends': 'debhelper-compat (= 13)', - 'Standards-Version': '4.5.0', - 'Section': 'libs', - 'Homepage': 'https://gitlab.com/', - # 'Vcs-Browser': 'https://salsa.debian.org/debian/sample-1.2.3', - # '#Vcs-Git': 'https://salsa.debian.org/debian/sample-1.2.3.git', - 'Rules-Requires-Root': 'no' + 'Source' => 'sample', + 'Priority' => 'optional', + 'Maintainer' => 'John Doe <john.doe@example.com>', + 'Build-Depends' => 'debhelper-compat (= 13)', + 'Standards-Version' => '4.5.0', + 'Section' => 'libs', + 'Homepage' => 'https://gitlab.com/', + # 'Vcs-Browser' => 'https://salsa.debian.org/debian/sample-1.2.3', + # '#Vcs-Git' => 'https://salsa.debian.org/debian/sample-1.2.3.git', + 'Rules-Requires-Root' => 'no' }, 'Package: sample-dev' => { - 'Package': 'sample-dev', - 'Section': 'libdevel', - 'Architecture': 'any', - 'Multi-Arch': 'same', - 'Depends': 'libsample0 (= ${binary:Version}), ${misc:Depends}', - 'Description': "Some mostly empty developpement files\nUsed in GitLab tests.\n\nTesting another paragraph." + 'Package' => 'sample-dev', + 'Section' => 'libdevel', + 'Architecture' => 'any', + 'Multi-Arch' => 'same', + 'Depends' => 'libsample0 (= ${binary:Version}), ${misc:Depends}', + 'Description' => "Some mostly empty developpement files\nUsed in GitLab tests.\n\nTesting another paragraph." }, 'Package: libsample0' => { - 'Package': 'libsample0', - 'Architecture': 'any', - 'Multi-Arch': 'same', - 'Depends': '${shlibs:Depends}, ${misc:Depends}', - 'Description': "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." + 'Package' => 'libsample0', + 'Architecture' => 'any', + 'Multi-Arch' => 'same', + 'Depends' => '${shlibs:Depends}, ${misc:Depends}', + 'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph." }, 'Package: sample-udeb' => { - 'Package': 'sample-udeb', - 'Package-Type': 'udeb', - 'Architecture': 'any', - 'Depends': 'installed-base', - 'Description': 'Some mostly empty udeb' + 'Package' => 'sample-udeb', + 'Package-Type' => 'udeb', + 'Architecture' => 'any', + 'Depends' => 'installed-base', + 'Description' => 'Some mostly empty udeb' } } diff --git a/spec/services/packages/debian/process_changes_service_spec.rb b/spec/services/packages/debian/process_changes_service_spec.rb new file mode 100644 index 00000000000..98b531bde10 --- /dev/null +++ b/spec/services/packages/debian/process_changes_service_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Debian::ProcessChangesService do + describe '#execute' do + let_it_be(:user) { create(:user) } + let_it_be_with_reload(:distribution) { create(:debian_project_distribution, :with_file, codename: 'unstable') } + let_it_be(:incoming) { create(:debian_incoming, project: distribution.project) } + + let(:package_file) { incoming.package_files.last } + + subject { described_class.new(package_file, user) } + + context 'with valid package file' do + it 'updates package and package file', :aggregate_failures do + expect { subject.execute } + .to change { Packages::Package.count }.from(1).to(2) + .and not_change { Packages::PackageFile.count } + .and change { incoming.package_files.count }.from(7).to(0) + + created_package = Packages::Package.last + expect(created_package.name).to eq 'sample' + expect(created_package.version).to eq '1.2.3~alpha2' + expect(created_package.creator).to eq user + end + end + + context 'with invalid package file' do + let(:package_file) { incoming.package_files.first } + + it 'raise ExtractionError', :aggregate_failures do + expect { subject.execute } + .to not_change { Packages::Package.count } + .and not_change { Packages::PackageFile.count } + .and not_change { incoming.package_files.count } + .and not_change { distribution.reload.needs_update? } + .and raise_error(Packages::Debian::ExtractChangesMetadataService::ExtractionError, 'is not a changes file') + end + end + + context 'when creating package fails' do + before do + allow_next_instance_of(::Packages::Debian::FindOrCreatePackageService) do |find_or_create_package_service| + expect(find_or_create_package_service).to receive(:execute).and_raise(ActiveRecord::ConnectionTimeoutError, 'connect timeout') + end + end + + it 'remove the package file', :aggregate_failures do + expect { subject.execute } + .to not_change { Packages::Package.count } + .and not_change { Packages::PackageFile.count } + .and not_change { incoming.package_files.count } + .and not_change { distribution.reload.needs_update? } + .and raise_error(ActiveRecord::ConnectionTimeoutError, 'connect timeout') + end + end + end +end diff --git a/spec/services/packages/go/create_package_service_spec.rb b/spec/services/packages/go/create_package_service_spec.rb new file mode 100644 index 00000000000..5c5fec0aa3a --- /dev/null +++ b/spec/services/packages/go/create_package_service_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Go::CreatePackageService do + let_it_be(:project) { create :project_empty_repo, path: 'my-go-lib' } + let_it_be(:mod) { create :go_module, project: project } + + before :all do + create :go_module_commit, :module, project: project, tag: 'v1.0.0' + end + + shared_examples 'creates a package' do |files:| + it "returns a valid package with #{files ? files.to_s : 'no'} file(s)" do + expect(subject).to be_valid + expect(subject.name).to eq(version.mod.name) + expect(subject.version).to eq(version.name) + expect(subject.package_type).to eq('golang') + expect(subject.created_at).to eq(version.commit.committed_date) + expect(subject.package_files.count).to eq(files) + end + end + + shared_examples 'creates a package file' do |type| + it "returns a package with a #{type} file" do + file_name = "#{version.name}.#{type}" + expect(subject.package_files.map { |f| f.file_name }).to include(file_name) + + file = subject.package_files.with_file_name(file_name).first + expect(file).not_to be_nil + expect(file.file).not_to be_nil + expect(file.size).to eq(file.file.size) + expect(file.file_name).to eq(file_name) + expect(file.file_md5).not_to be_nil + expect(file.file_sha1).not_to be_nil + expect(file.file_sha256).not_to be_nil + end + end + + describe '#execute' do + subject { described_class.new(project, nil, version: version).execute } + + let(:version) { create :go_module_version, :tagged, mod: mod, name: 'v1.0.0' } + + context 'with no existing package' do + it_behaves_like 'creates a package', files: 2 + it_behaves_like 'creates a package file', :mod + it_behaves_like 'creates a package file', :zip + + it 'creates a new package' do + expect { subject } + .to change { project.packages.count }.by(1) + .and change { Packages::PackageFile.count }.by(2) + end + end + + context 'with an existing package' do + before do + described_class.new(project, version: version).execute + end + + it_behaves_like 'creates a package', files: 2 + it_behaves_like 'creates a package file', :mod + it_behaves_like 'creates a package file', :zip + + it 'does not create a package or files' do + expect { subject } + .to not_change { project.packages.count } + .and not_change { Packages::PackageFile.count } + end + end + end +end diff --git a/spec/services/packages/go/sync_packages_service_spec.rb b/spec/services/packages/go/sync_packages_service_spec.rb new file mode 100644 index 00000000000..565b0f252ce --- /dev/null +++ b/spec/services/packages/go/sync_packages_service_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Go::SyncPackagesService do + include_context 'basic Go module' + + let(:params) { { info: true, mod: true, zip: true } } + + describe '#execute_async' do + it 'schedules a package refresh' do + expect(::Packages::Go::SyncPackagesWorker).to receive(:perform_async).once + + described_class.new(project, 'master').execute_async + end + end + + describe '#initialize' do + context 'without a project' do + it 'raises an error' do + expect { described_class.new(nil, 'master') } + .to raise_error(ArgumentError, 'project is required') + end + end + + context 'without a ref' do + it 'raises an error' do + expect { described_class.new(project, nil) } + .to raise_error(ArgumentError, 'ref is required') + end + end + + context 'with an invalid ref' do + it 'raises an error' do + expect { described_class.new(project, 'not-a-ref') } + .to raise_error(ArgumentError) + end + end + end +end diff --git a/spec/services/packages/maven/metadata/sync_service_spec.rb b/spec/services/packages/maven/metadata/sync_service_spec.rb index f5634159e6d..30ddb48207a 100644 --- a/spec/services/packages/maven/metadata/sync_service_spec.rb +++ b/spec/services/packages/maven/metadata/sync_service_spec.rb @@ -131,7 +131,7 @@ RSpec.describe ::Packages::Maven::Metadata::SyncService do expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) end - it_behaves_like 'returning an error service response', message: 'Non existing versionless package' + it_behaves_like 'returning a success service response', message: 'Non existing versionless package(s). Nothing to do.' end context 'without a metadata package file for versions' do @@ -141,7 +141,7 @@ RSpec.describe ::Packages::Maven::Metadata::SyncService do 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' + it_behaves_like 'returning a success service response', message: 'Non existing versionless package(s). Nothing to do.' end context 'without a project' do @@ -205,7 +205,7 @@ RSpec.describe ::Packages::Maven::Metadata::SyncService do 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 + context 'with a too big maven metadata file for plugins' do before do metadata_file_for_plugins.update!(size: 100.megabytes) end @@ -244,6 +244,15 @@ RSpec.describe ::Packages::Maven::Metadata::SyncService do it_behaves_like 'returning a success service response', message: 'New metadata package files created' end + context 'without a versionless package for versions' do + before do + versionless_package_for_versions.package_files.update_all(file_name: 'test.txt') + expect(::Packages::Maven::Metadata::CreateVersionsXmlService).not_to receive(:new) + end + + it_behaves_like 'returning a success service response', message: 'No changes for plugins xml' + end + context 'without a metadata package file for plugins' do before do versionless_package_for_plugins.package_files.update_all(file_name: 'test.txt') diff --git a/spec/services/packages/rubygems/create_dependencies_service_spec.rb b/spec/services/packages/rubygems/create_dependencies_service_spec.rb new file mode 100644 index 00000000000..b6e12b1cc61 --- /dev/null +++ b/spec/services/packages/rubygems/create_dependencies_service_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Rubygems::CreateDependenciesService do + include RubygemsHelpers + + let_it_be(:package) { create(:rubygems_package) } + let_it_be(:package_file) { create(:package_file, :gem) } + let_it_be(:gem) { gem_from_file(package_file.file) } + let_it_be(:gemspec) { gem.spec } + + let(:service) { described_class.new(package, gemspec) } + + describe '#execute' do + subject { service.execute } + + it 'creates dependencies', :aggregate_failures do + expect { subject }.to change { Packages::Dependency.count }.by(4) + + gemspec.dependencies.each do |dependency| + persisted_dependency = Packages::Dependency.find_by(name: dependency.name) + + expect(persisted_dependency.version_pattern).to eq dependency.requirement.to_s + end + end + + it 'links dependencies to the package' do + expect { subject }.to change { package.dependency_links.count }.by(4) + + expect(package.dependency_links.first).to be_dependencies + end + end +end diff --git a/spec/services/packages/rubygems/create_gemspec_service_spec.rb b/spec/services/packages/rubygems/create_gemspec_service_spec.rb new file mode 100644 index 00000000000..198e978a47e --- /dev/null +++ b/spec/services/packages/rubygems/create_gemspec_service_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Rubygems::CreateGemspecService do + include RubygemsHelpers + + let_it_be(:package_file) { create(:package_file, :gem) } + let_it_be(:gem) { gem_from_file(package_file.file) } + let_it_be(:gemspec) { gem.spec } + let_it_be(:package) { package_file.package } + + let(:service) { described_class.new(package, gemspec) } + + describe '#execute' do + subject { service.execute } + + it 'creates a new package file', :aggregate_failures do + expect { subject }.to change { package.package_files.count }.by(1) + + gemspec_file = package.package_files.find_by(file_name: "#{gemspec.name}.gemspec") + expect(gemspec_file.file).not_to be_nil + expect(gemspec_file.size).not_to be_nil + expect(gemspec_file.file_md5).not_to be_nil + expect(gemspec_file.file_sha1).not_to be_nil + expect(gemspec_file.file_sha256).not_to be_nil + end + end +end diff --git a/spec/services/packages/rubygems/metadata_extraction_service_spec.rb b/spec/services/packages/rubygems/metadata_extraction_service_spec.rb new file mode 100644 index 00000000000..b308daad8f5 --- /dev/null +++ b/spec/services/packages/rubygems/metadata_extraction_service_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true +require 'spec_helper' +require 'rubygems/package' + +RSpec.describe Packages::Rubygems::MetadataExtractionService do + include RubygemsHelpers + + let_it_be(:package) { create(:rubygems_package) } + let_it_be(:package_file) { create(:package_file, :gem) } + let_it_be(:gem) { gem_from_file(package_file.file) } + let_it_be(:gemspec) { gem.spec } + + let(:service) { described_class.new(package, gemspec) } + + describe '#execute' do + subject { service.execute } + + it 'creates the metadata' do + expect { subject }.to change { Packages::Rubygems::Metadatum.count }.by(1) + end + + it 'stores the metadata', :aggregate_failures do + subject + + metadata = package.rubygems_metadatum + + expect(metadata.authors).to eq(gemspec.authors.to_json) + expect(metadata.files).to eq(gemspec.files.to_json) + expect(metadata.summary).to eq(gemspec.summary) + expect(metadata.description).to eq(gemspec.description) + expect(metadata.email).to eq(gemspec.email) + expect(metadata.homepage).to eq(gemspec.homepage) + expect(metadata.licenses).to eq(gemspec.licenses.to_json) + expect(metadata.metadata).to eq(gemspec.metadata.to_json) + expect(metadata.author).to eq(gemspec.author) + expect(metadata.bindir).to eq(gemspec.bindir) + expect(metadata.executables).to eq(gemspec.executables.to_json) + expect(metadata.extensions).to eq(gemspec.extensions.to_json) + expect(metadata.extra_rdoc_files).to eq(gemspec.extra_rdoc_files.to_json) + expect(metadata.platform).to eq(gemspec.platform) + expect(metadata.post_install_message).to eq(gemspec.post_install_message) + expect(metadata.rdoc_options).to eq(gemspec.rdoc_options.to_json) + expect(metadata.require_paths).to eq(gemspec.require_paths.to_json) + expect(metadata.required_ruby_version).to eq(gemspec.required_ruby_version.to_s) + expect(metadata.required_rubygems_version).to eq(gemspec.required_rubygems_version.to_s) + expect(metadata.requirements).to eq(gemspec.requirements.to_json) + expect(metadata.rubygems_version).to eq(gemspec.rubygems_version) + end + end +end diff --git a/spec/services/packages/rubygems/process_gem_service_spec.rb b/spec/services/packages/rubygems/process_gem_service_spec.rb new file mode 100644 index 00000000000..83e868d9579 --- /dev/null +++ b/spec/services/packages/rubygems/process_gem_service_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Rubygems::ProcessGemService do + include ExclusiveLeaseHelpers + include RubygemsHelpers + + let_it_be_with_reload(:package) { create(:rubygems_package, :processing, name: 'temp_name', version: '0.0.0') } + + let(:package_file) { create(:package_file, :unprocessed_gem, package: package) } + let(:gem) { gem_from_file(package_file.file) } + let(:gemspec) { gem.spec } + let(:service) { described_class.new(package_file) } + + describe '#execute' do + subject { service.execute } + + context 'no gem file', :aggregate_failures do + let(:package_file) { nil } + + it 'returns an error' do + expect(subject.error?).to be(true) + expect(subject.message).to eq('Gem was not processed') + end + end + + context 'success' do + let(:sub_service) { double } + + before do + expect(Packages::Rubygems::MetadataExtractionService).to receive(:new).with(package, gemspec).and_return(sub_service) + expect(Packages::Rubygems::CreateGemspecService).to receive(:new).with(package, gemspec).and_return(sub_service) + expect(Packages::Rubygems::CreateDependenciesService).to receive(:new).with(package, gemspec).and_return(sub_service) + + expect(sub_service).to receive(:execute).exactly(3).times.and_return(true) + end + + it 'returns successfully', :aggregate_failures do + result = subject + + expect(result.success?).to be true + expect(result.payload[:package]).to eq(package) + end + + it 'updates the package name and version', :aggregate_failures do + expect(package.name).to eq('temp_name') + expect(package.version).to eq('0.0.0') + expect(package).to be_processing + + subject + + expect(package.reload.name).to eq('package') + expect(package.version).to eq('0.0.1') + expect(package).to be_default + end + + it 'updates the package file name', :aggregate_failures do + expect(package_file.file_name).to eq('package.gem') + + subject + + expect(package_file.reload.file_name).to eq('package-0.0.1.gem') + end + end + + context 'when the package already exists' do + let_it_be(:existing_package) { create(:rubygems_package, name: 'package', version: '0.0.1', project: package.project) } + + let(:sub_service) { double } + + before do + expect(Packages::Rubygems::MetadataExtractionService).to receive(:new).with(existing_package, gemspec).and_return(sub_service) + expect(Packages::Rubygems::CreateGemspecService).to receive(:new).with(existing_package, gemspec).and_return(sub_service) + expect(Packages::Rubygems::CreateDependenciesService).to receive(:new).with(existing_package, gemspec).and_return(sub_service) + + expect(sub_service).to receive(:execute).exactly(3).times.and_return(true) + end + + it 'assigns the package_file to the existing package and deletes the temporary package', :aggregate_failures do + expect(package).to receive(:destroy) + + expect { subject }.to change { existing_package.package_files.count }.by(1) + + expect(package_file.reload.package).to eq(existing_package) + end + end + + context 'sub-service failure' do + before do + expect(Packages::Rubygems::MetadataExtractionService).to receive(:new).with(package, gemspec).and_raise(::Packages::Rubygems::ProcessGemService::ExtractionError.new('failure')) + end + + it 'returns an error' do + expect { subject }.to raise_error(::Packages::Rubygems::ProcessGemService::ExtractionError, 'failure') + end + end + + context 'bad gem file' do + before do + expect(Gem::Package).to receive(:new).and_raise(ArgumentError) + end + + it 'returns an error' do + expect { subject }.to raise_error(::Packages::Rubygems::ProcessGemService::ExtractionError, 'Unable to read gem file') + end + end + + context 'without obtaining an exclusive lease' do + let(:lease_key) { "packages:rubygems:process_gem_service:package:#{package.id}" } + + before do + stub_exclusive_lease_taken(lease_key, timeout: 1.hour) + end + + it 'does not perform the services', :aggregate_failures do + # The #use_file call triggers a separate lease on the package file being opened + # for use with the gem. We don't want to test that here, so we allow the call to proceed + expect(Gitlab::ExclusiveLease).to receive(:new).with("object_storage_migrate:Packages::PackageFile:#{package_file.id}", anything).and_call_original + + expect(Packages::Rubygems::MetadataExtractionService).not_to receive(:new) + expect(Packages::Rubygems::CreateGemspecService).not_to receive(:new) + expect(Packages::Rubygems::CreateDependenciesService).not_to receive(:new) + + subject + + expect(package.reload.name).to eq('temp_name') + expect(package.version).to eq('0.0.0') + expect(package).to be_processing + expect(package_file.reload.file_name).to eq('package.gem') + end + end + end +end |