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