spec/lib/release_tools/product_version_spec.rb (355 lines of code) (raw):

# frozen_string_literal: true require 'spec_helper' describe ReleaseTools::ProductVersion do include MetadataHelper def version(version_string) described_class.new(version_string) end describe "to_s" do it 'is a string representation of the given version' do expect(version('1.2.3').to_s).to eq('1.2.3') end end describe '.new' do it { expect(version('14.2.3')).to eq(version('14.2.3')) } # rubocop:disable RSpec/IdenticalEqualityAssertion it { expect(version('14.2')).to eq(version('14.2.0')) } it { expect(version('14.2.0-rc42')).to eq(version('14.2.0-rc42')) } # rubocop:disable RSpec/IdenticalEqualityAssertion it { expect(version('14.4.202110071021')).to eq(version('14.4.202110071021')) } # rubocop:disable RSpec/IdenticalEqualityAssertion end describe '.from_auto_deploy' do it 'returns nil when not an auto_deploy package' do expect(described_class.from_auto_deploy('14.3.0')).to be_nil end it 'returns the normalized version for an auto_deploy tag' do version = described_class.from_auto_deploy('14.4.202110080320+c92f14192f5.e2ad57eb8ba') expect(version).to eq(version('14.4.202110080320')) end it 'returns the normalized version for an auto_deploy omnibus version' do version = described_class.from_auto_deploy('14.4.202110080320-c92f14192f5.e2ad57eb8ba') expect(version).to eq(version('14.4.202110080320')) end end describe '.from_auto_deploy_tag' do it 'returns nil when not an auto_deploy tag' do expect(described_class.from_auto_deploy_tag('14.3.0')).to be_nil end it 'returns the normalized version for an auto_deploy Omnibus tag' do version = described_class.from_auto_deploy_tag('14.4.202110080320+c92f14192f5.e2ad57eb8ba') expect(version).to eq(version('14.4.202110080320')) end it 'returns the normalized version for an auto_deploy CNG tag' do version = described_class.from_auto_deploy_tag('14.4.202110080320+c92f14192f5') expect(version).to eq(version('14.4.202110080320')) end end describe '.from_package_version' do it 'returns nil when not a valid version' do expect(described_class.from_package_version('foo.bar+baz')).to be_nil end it 'returns the normalized version for a monthly release' do version = described_class.from_package_version('14.4.0-ee') expect(version).to eq(version('14.4.0')) end it 'returns the normalized version for a patch release' do version = described_class.from_package_version('14.4.1') expect(version).to eq(version('14.4.1')) end it 'returns the normalized version for an auto_deploy omnibus version' do version = described_class.from_package_version('14.4.202110080320-c92f14192f5.e2ad57eb8ba') expect(version).to eq(version('14.4.202110080320')) end end describe '.from_metadata_sha' do context 'when the metadata exists' do it 'returns the correct product version' do release = ReleaseTools::Version.new('14.0.1') metadata_commit_id = 'abc123' metadata_path = 'releases/14/14.0.1.json' diff = create(:diff, new_path: metadata_path, new_file: true) expect(ReleaseTools::GitlabOpsClient) .to receive(:commit_diff) .with( ReleaseTools::ReleaseMetadataUploader::PROJECT, metadata_commit_id ) .and_return([diff]) expect(described_class.from_metadata_sha(metadata_commit_id)).to eq(release) end end context 'when the metadata commit does not exist' do it 'returns nil' do metadata_commit_id = 'abc123' request_double = double(base_uri: 'https://gitlab.com/api/v4', path: '/something', options: {}) response_double = double('response', parsed_response: { message: 'Not found' }, code: 404, request: request_double) expect(ReleaseTools::GitlabOpsClient) .to receive(:commit_diff) .with( ReleaseTools::ReleaseMetadataUploader::PROJECT, metadata_commit_id ) .and_raise(Gitlab::Error::NotFound, response_double) expect(described_class.from_metadata_sha(metadata_commit_id)).to be_nil end end end context 'Enumerable implementation' do let(:versions) { build_list(:product_version, 5) } before do allow(described_class).to receive(:lazy_enumerator).and_return(versions.each.lazy) end describe '.each' do it 'yields product versions' do expect { |b| described_class.each(&b) }.to yield_successive_args(*versions) end end describe '.find' do let(:security_version) { build(:product_version, security: true) } let(:versions) { build_list(:product_version, 2) << security_version } it 'finds product version based on a given block' do expect( described_class.find do |version| version.metadata['security'] == true end ).to eq(security_version) end end end describe '.lazy_enumerator' do subject(:lazy_enumerator) { described_class.each } it 'fetches commits page by page' do first_commits = [build(:release_metadata_commit), build(:release_metadata_commit)] first_product_version1 = build(:product_version, metadata_commit_id: first_commits.first.id) first_product_version2 = build(:product_version, metadata_commit_id: first_commits.last.id) page_two = Gitlab::PaginatedResponse.new(first_commits) last_commits = [build(:release_metadata_commit), build(:release_metadata_commit)] last_product_version1 = build(:product_version, metadata_commit_id: last_commits.first.id) last_product_version2 = build(:product_version, metadata_commit_id: last_commits.last.id) page_one = Gitlab::PaginatedResponse.new(last_commits) allow(page_one).to receive(:next_page).and_return(page_two) expect(ReleaseTools::GitlabOpsClient) .to receive(:commits) .with(ReleaseTools::ReleaseMetadataUploader::PROJECT, trailers: true) .once .and_return(page_one) expect(described_class) .to receive(:new) .with(last_commits.first.trailers['Product-Version']) .and_return(last_product_version1) expect(described_class) .to receive(:new) .with(last_commits.last.trailers['Product-Version']) .and_return(last_product_version2) expect(lazy_enumerator.next).to eq(last_product_version1) expect(lazy_enumerator.next).to eq(last_product_version2) expect(described_class) .to receive(:new) .with(first_commits.first.trailers['Product-Version']) .and_return(first_product_version1) expect(described_class) .to receive(:new) .with(first_commits.last.trailers['Product-Version']) .and_return(first_product_version2) expect(page_one).to receive(:has_next_page?).and_return(true) expect(lazy_enumerator.next).to eq(first_product_version1) expect(lazy_enumerator.next).to eq(first_product_version2) expect(page_two).to receive(:has_next_page?).and_return(false) expect { lazy_enumerator.next }.to raise_exception(StopIteration) end it 'processes commits lazily' do last_commits = [build(:release_metadata_commit), build(:release_metadata_commit)] last_product_version = build(:product_version, metadata_commit_id: last_commits.first.id) page_one = Gitlab::PaginatedResponse.new(last_commits) expect(ReleaseTools::GitlabOpsClient) .to receive(:commits) .with(ReleaseTools::ReleaseMetadataUploader::PROJECT, trailers: true) .once .and_return(page_one) expect(page_one).not_to receive(:has_next_page?) expect(page_one).not_to receive(:next_page) expect(described_class) .to receive(:new) .with(last_commits.first.trailers['Product-Version']) .and_return(last_product_version) expect(described_class).not_to receive(:new).with(last_commits.last.trailers['Product-Version']) expect(lazy_enumerator.next).to eq(last_product_version) end it 'ignores commits without Product-Version trailer' do last_commits = [build(:commit), build(:release_metadata_commit)] last_product_version = build(:product_version, metadata_commit_id: last_commits.last.id) page_one = Gitlab::PaginatedResponse.new(last_commits) expect(ReleaseTools::GitlabOpsClient) .to receive(:commits) .with(ReleaseTools::ReleaseMetadataUploader::PROJECT, trailers: true) .once .and_return(page_one) expect(described_class) .to receive(:new) .with(last_commits.last.trailers['Product-Version']) .and_return(last_product_version) expect(lazy_enumerator.next).to eq(last_product_version) end end describe '.last_auto_deploy' do let(:auto_deploy_version) { build(:product_version) } let(:versions) { build_list(:product_version, 2, version: "15.5.5") << auto_deploy_version } it 'finds the most recent auto_deploy version' do allow(described_class).to receive(:lazy_enumerator).and_return(versions.each.lazy) expect( described_class.last_auto_deploy ).to eq(auto_deploy_version) end end describe '#monthly?' do it 'is true for monthly releases' do expect(version('14.2.0')).to be_monthly end it 'is false for patch releases' do expect(version('14.2.3')).not_to be_monthly end it 'is false for pre-releases' do expect(version('14.2.0-rc42')).not_to be_monthly end end describe '#patch?' do it 'is true for patch releases' do expect(version('14.2.3')).to be_patch end it 'is false for pre-releases' do expect(version('14.2.0-rc1')).not_to be_patch end it 'is false for minor releases' do expect(version('14.2.0')).not_to be_patch end it 'is false for invalid releases' do expect(version('wow.1')).not_to be_patch end end describe '#major' do it { expect(version('14.2.3').major).to eq(14) } it { expect(version('14.2.0-rc1').major).to eq(14) } it { expect(version('wow.1').major).to eq(0) } end describe '#minor' do it { expect(version('14.2.3').minor).to eq(2) } it { expect(version('14.2.0-rc1').minor).to eq(2) } it { expect(version('wow.1').minor).to eq(0) } end describe '#rc' do it { expect(version('14.2.3-rc').rc).to eq(0) } it { expect(version('14.2.3-rc42').rc).to eq(42) } it { expect(version('14.2.3').rc).to be_nil } it { expect(version('wow-rc1').rc).to be_nil } end describe '#rc?' do it { expect(version('14.2.3-rc')).to be_rc } it { expect(version('14.2.3-rc1')).to be_rc } it { expect(version('14.2.3')).not_to be_rc } it { expect(version('wow-rc1')).not_to be_rc } end describe '#auto_deploy?' do it { expect(version('14.2.3-rc')).not_to be_auto_deploy } it { expect(version('14.2.3-rc1')).not_to be_auto_deploy } it { expect(version('14.2.3')).not_to be_auto_deploy } it { expect(version('wow-rc1')).not_to be_auto_deploy } it { expect(version('14.6.202112160920')).to be_auto_deploy } it { expect(version('14.6.202112161020')).to be_auto_deploy } end describe '#<=>' do it { expect(version('14.6.202112160920') < version('14.6.202112161020')).to be_truthy } it { expect(version('14.10.202112160620') > version('14.9.202112160920')).to be_truthy } end describe '#metadata' do context 'when the metadata exists' do it 'returns the metadata as a Hash' do release = '14.0.1' metadata = { 'some' => 'data' } allow(ReleaseTools::GitlabOpsClient) .to receive(:get_file) .with( ReleaseTools::ReleaseMetadataUploader::PROJECT, 'releases/14/14.0.1.json', ReleaseTools::ReleaseMetadataUploader::PROJECT.default_branch ) .and_return(Gitlab::ObjectifiedHash.new( last_commit_id: 'abc', content: Base64.strict_encode64(JSON.dump(metadata)) )) expect(version(release).metadata).to eq(metadata) end end context 'when the metadata does not exist' do it 'returns an empty Hash' do release = '14.0.1' allow(ReleaseTools::GitlabOpsClient) .to receive(:get_file) .with( ReleaseTools::ReleaseMetadataUploader::PROJECT, 'releases/14/14.0.1.json', ReleaseTools::ReleaseMetadataUploader::PROJECT.default_branch ) .and_raise(gitlab_error(:NotFound)) expect(version(release).metadata).to eq({}) end end end describe '#[]' do subject(:product_version) { described_class.new('13.0.202005121540') } let(:metadata) do { 'releases' => { 'omnibus-gitlab-ee' => { 'version' => 'cb5ec4eab2cfc0a2c02c66ffb47ff782c226e5b0', 'sha' => 'cb5ec4eab2cfc0a2c02c66ffb47ff782c226e5b0', 'ref' => '13-0-auto-deploy-20200512', 'tag' => false } } } end before do allow(product_version).to receive(:metadata).and_return(metadata) end it 'returns metadata for the given component' do omnibus = product_version['omnibus-gitlab-ee'] expect(omnibus).not_to be_nil expected_meta = metadata.dig('releases', 'omnibus-gitlab-ee') expect(omnibus.name).to eq('omnibus-gitlab-ee') expect(omnibus.version).to eq(expected_meta['version']) expect(omnibus.sha).to eq(expected_meta['sha']) expect(omnibus.ref).to eq(expected_meta['ref']) expect(omnibus.tag).to eq(expected_meta['tag']) end it 'returns metadata for the given component class' do omnibus = product_version[ReleaseTools::Project::OmnibusGitlab] expect(omnibus).not_to be_nil expected_meta = metadata.dig('releases', 'omnibus-gitlab-ee') expect(omnibus.name).to eq('omnibus-gitlab-ee') expect(omnibus.version).to eq(expected_meta['version']) expect(omnibus.sha).to eq(expected_meta['sha']) expect(omnibus.ref).to eq(expected_meta['ref']) expect(omnibus.tag).to eq(expected_meta['tag']) end it 'returns nil when the component does not exists' do expect(product_version['foo-bar']).to be_nil end end describe '#auto_deploy_package' do subject(:product_version) do described_class.new('14.8.202202091820') end before do metadata = build_metadata( auto_deploy_branch: '14.8.202202091820', gitlab_sha: '74f805e6aa4', omnibus_sha: '15e30c3a2fe', tag: true ) allow(product_version) .to receive(:metadata) .and_return(metadata) end it 'returns auto-deploy package' do expect(product_version.auto_deploy_package) .to eq('14.8.202202091820-74f805e6aa4.15e30c3a2fe') end end end