diff options
Diffstat (limited to 'spec/lib/sbom/package_url')
-rw-r--r-- | spec/lib/sbom/package_url/argument_validator_spec.rb | 51 | ||||
-rw-r--r-- | spec/lib/sbom/package_url/decoder_spec.rb | 121 | ||||
-rw-r--r-- | spec/lib/sbom/package_url/encoder_spec.rb | 29 | ||||
-rw-r--r-- | spec/lib/sbom/package_url/normalizer_spec.rb | 76 |
4 files changed, 277 insertions, 0 deletions
diff --git a/spec/lib/sbom/package_url/argument_validator_spec.rb b/spec/lib/sbom/package_url/argument_validator_spec.rb new file mode 100644 index 00000000000..246da1c0bda --- /dev/null +++ b/spec/lib/sbom/package_url/argument_validator_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' + +require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts' + +RSpec.describe Sbom::PackageUrl::ArgumentValidator do + let(:mock_package_url) { Struct.new(:type, :namespace, :name, :version, :qualifiers, keyword_init: true) } + let(:package) do + mock_package_url.new( + type: type, + namespace: namespace, + name: name, + version: version, + qualifiers: qualifiers + ) + end + + subject(:validate) { described_class.new(package).validate! } + + context 'with valid arguments' do + include_context 'with valid purl examples' + + with_them do + it 'does not raise error' do + expect { validate }.not_to raise_error + end + end + end + + context 'with invalid arguments' do + include_context 'with invalid purl examples' + + with_them do + it 'raises an ArgumentError' do + expect { validate }.to raise_error(ArgumentError) + end + end + end + + context 'with multiple errors' do + let(:type) { nil } + let(:name) { nil } + let(:package) { mock_package_url.new(type: type, name: name) } + + it 'reports all errors' do + expect { validate }.to raise_error(ArgumentError, 'Type is required, Name is required') + end + end +end diff --git a/spec/lib/sbom/package_url/decoder_spec.rb b/spec/lib/sbom/package_url/decoder_spec.rb new file mode 100644 index 00000000000..5b480475b7c --- /dev/null +++ b/spec/lib/sbom/package_url/decoder_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' + +require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts' + +RSpec.describe Sbom::PackageUrl::Decoder do + describe '#decode' do + subject(:decode) { described_class.new(purl).decode! } + + include_context 'with valid purl examples' + + with_them do + it do + is_expected.to have_attributes( + type: type, + namespace: namespace, + name: name, + version: version, + qualifiers: qualifiers, + subpath: subpath + ) + end + end + + context 'when no argument is passed' do + let(:purl) { nil } + + it 'raises an error' do + expect { decode }.to raise_error(ArgumentError) + end + end + + context 'when an invalid package URL string is passed' do + include_context 'with invalid purl examples' + + with_them do + it 'raises an error' do + expect { decode }.to raise_error(Sbom::PackageUrl::InvalidPackageUrl) + end + end + end + + context 'when namespace or subpath contains an encoded slash' do + where(:purl) do + [ + 'pkg:golang/google.org/golang/genproto#googleapis%2fapi%2fannotations', + 'pkg:golang/google.org%2fgolang/genproto#googleapis/api/annotations' + ] + end + + with_them do + it { expect { decode }.to raise_error(Sbom::PackageUrl::InvalidPackageUrl) } + end + end + + context 'when name contains an encoded slash' do + let(:purl) { 'pkg:golang/google.org/golang%2fgenproto#googleapis/api/annotations' } + + it do + is_expected.to have_attributes( + type: 'golang', + namespace: 'google.org', + name: 'golang/genproto', + version: nil, + qualifiers: nil, + subpath: 'googleapis/api/annotations' + ) + end + end + + context 'with URL encoded segments' do + let(:purl) do + 'pkg:golang/namespace%21/google.golang.org%20genproto@version%21?k=v%21#googleapis%20api%20annotations' + end + + it 'decodes them' do + is_expected.to have_attributes( + type: 'golang', + namespace: 'namespace!', + name: 'google.golang.org genproto', + version: 'version!', + qualifiers: { 'k' => 'v!' }, + subpath: 'googleapis api annotations' + ) + end + end + + context 'when segments contain empty values' do + let(:purl) { 'pkg:golang/google.golang.org//.././genproto#googleapis/..//./api/annotations' } + + it 'removes them from the segments' do + is_expected.to have_attributes( + type: 'golang', + namespace: 'google.golang.org/../.', # . and .. are allowed in the namespace, but not the subpath + name: 'genproto', + version: nil, + qualifiers: nil, + subpath: 'googleapis/api/annotations' + ) + end + end + + context 'when qualifiers have no value' do + let(:purl) { 'pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25&foo=&bar=' } + + it 'they are ignored' do + is_expected.to have_attributes( + type: 'rpm', + namespace: 'fedora', + name: 'curl', + version: '7.50.3-1.fc25', + qualifiers: { 'arch' => 'i386', + 'distro' => 'fedora-25' }, + subpath: nil + ) + end + end + end +end diff --git a/spec/lib/sbom/package_url/encoder_spec.rb b/spec/lib/sbom/package_url/encoder_spec.rb new file mode 100644 index 00000000000..bdbd61636b5 --- /dev/null +++ b/spec/lib/sbom/package_url/encoder_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' + +require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts' + +RSpec.describe Sbom::PackageUrl::Encoder do + describe '#encode' do + let(:package) do + ::Sbom::PackageUrl.new( + type: type, + namespace: namespace, + name: name, + version: version, + qualifiers: qualifiers, + subpath: subpath + ) + end + + subject(:encode) { described_class.new(package).encode } + + include_context 'with valid purl examples' + + with_them do + it { is_expected.to eq(canonical_purl) } + end + end +end diff --git a/spec/lib/sbom/package_url/normalizer_spec.rb b/spec/lib/sbom/package_url/normalizer_spec.rb new file mode 100644 index 00000000000..bbc2bd3ca13 --- /dev/null +++ b/spec/lib/sbom/package_url/normalizer_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require 'rspec-parameterized' + +require_relative '../../../support/shared_contexts/lib/sbom/package_url_shared_contexts' + +RSpec.describe Sbom::PackageUrl::Normalizer do + shared_examples 'name normalization' do + context 'with bitbucket url' do + let(:type) { 'bitbucket' } + let(:text) { 'Purl_Spec' } + + it 'downcases text' do + is_expected.to eq('purl_spec') + end + end + + context 'with github url' do + let(:type) { 'github' } + let(:text) { 'Purl_Spec' } + + it 'downcases text' do + is_expected.to eq('purl_spec') + end + end + + context 'with pypi url' do + let(:type) { 'pypi' } + let(:text) { 'Purl_Spec' } + + it 'downcases text and replaces underscores' do + is_expected.to eq('purl-spec') + end + end + + context 'with other urls' do + let(:type) { 'npm' } + let(:text) { 'Purl_Spec' } + + it 'does not change the text' do + is_expected.to eq(text) + end + end + end + + describe '#normalize_name' do + subject(:normalize_name) { described_class.new(type: type, text: text).normalize_name } + + it_behaves_like 'name normalization' + + context 'when text is nil' do + let(:type) { 'npm' } + let(:text) { nil } + + it 'raises an error' do + expect { normalize_name }.to raise_error(ArgumentError, 'Name is required') + end + end + end + + describe '#normalize_namespace' do + subject(:normalize_namespace) { described_class.new(type: type, text: text).normalize_namespace } + + it_behaves_like 'name normalization' + + context 'when text is nil' do + let(:type) { 'npm' } + let(:text) { nil } + + it 'allows nil values' do + expect(normalize_namespace).to be_nil + end + end + end +end |