From c408330e8bd55246b3400cf75d83860d72846b2e Mon Sep 17 00:00:00 2001 From: Holly Gong Date: Fri, 23 Aug 2024 12:22:51 +1000 Subject: [PATCH 1/2] fix: handle invalid version input in ecosystem sort_key --- osv/ecosystems/alpine.py | 4 ++++ osv/ecosystems/alpine_test.py | 6 ++++++ osv/ecosystems/bioconductor.py | 4 ++++ osv/ecosystems/bioconductor_test.py | 1 + osv/ecosystems/cran.py | 1 + osv/ecosystems/haskell.py | 7 ++++++- osv/ecosystems/haskell_test.py | 4 ++++ osv/ecosystems/maven_test.py | 4 +++- osv/ecosystems/nuget.py | 4 ++++ osv/ecosystems/nuget_test.py | 5 +++++ osv/ecosystems/packagist_test.py | 2 ++ osv/ecosystems/pub.py | 7 ++++++- osv/ecosystems/pub_test.py | 2 ++ osv/ecosystems/pypi.py | 1 + osv/ecosystems/pypi_test.py | 6 ++++++ osv/ecosystems/rocky_linux_test.py | 15 ++++++++------- osv/ecosystems/rubygems.py | 10 ++++++++-- osv/ecosystems/rubygems_test.py | 6 ++++++ osv/ecosystems/ubuntu_test.py | 14 ++++++++------ 19 files changed, 85 insertions(+), 18 deletions(-) diff --git a/osv/ecosystems/alpine.py b/osv/ecosystems/alpine.py index 5ee871712bf..766a81058aa 100644 --- a/osv/ecosystems/alpine.py +++ b/osv/ecosystems/alpine.py @@ -53,6 +53,10 @@ def get_branch_name(self) -> str: return self.alpine_release_ver.lstrip('v') + self._BRANCH_SUFFIX def sort_key(self, version): + if not AlpineLinuxVersion.is_valid(version): + # If version is not valid, it is most likely an invalid input + # version then sort it to the last/largest element + return AlpineLinuxVersion('999999') return AlpineLinuxVersion(version) @staticmethod diff --git a/osv/ecosystems/alpine_test.py b/osv/ecosystems/alpine_test.py index 1f193719880..13c316be4bd 100644 --- a/osv/ecosystems/alpine_test.py +++ b/osv/ecosystems/alpine_test.py @@ -61,4 +61,10 @@ def test_alpine(self, ensure_updated_checkout_mock: mock.MagicMock): self.assertGreater( ecosystem.sort_key('1.13.2-r0'), ecosystem.sort_key('1.13.2_alpha')) + # Check invalid version handle + print(ecosystem.sort_key('1-0-0')) + self.assertGreater( + ecosystem.sort_key('1-0-0'), ecosystem.sort_key('1.13.2-r0') + ) + ecosystems.config.set_cache(None) diff --git a/osv/ecosystems/bioconductor.py b/osv/ecosystems/bioconductor.py index fb58cdd2df0..2dafd63d5c4 100644 --- a/osv/ecosystems/bioconductor.py +++ b/osv/ecosystems/bioconductor.py @@ -44,6 +44,10 @@ def get_bioc_versions(self): def sort_key(self, version): """Sort key.""" + if not semver_index.is_valid(version): + # If version is not valid, it is most likely an invalid input + # version then sort it to the last/largest element + return semver_index.parse('999999') return semver_index.parse(version) def _enumerate_versions(self, diff --git a/osv/ecosystems/bioconductor_test.py b/osv/ecosystems/bioconductor_test.py index 4484c867e52..5aeafa191c2 100644 --- a/osv/ecosystems/bioconductor_test.py +++ b/osv/ecosystems/bioconductor_test.py @@ -26,5 +26,6 @@ def test_next_version(self): ecosystem = ecosystems.get('Bioconductor') self.assertEqual('1.18.0', ecosystem.next_version('a4', '1.16.0')) self.assertEqual('1.20.0', ecosystem.next_version('a4', '1.18.0')) + self.assertGreater(ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0')) with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') diff --git a/osv/ecosystems/cran.py b/osv/ecosystems/cran.py index cb446135e21..782577203b9 100644 --- a/osv/ecosystems/cran.py +++ b/osv/ecosystems/cran.py @@ -37,6 +37,7 @@ def sort_key(self, version): # The packaging.version appears to work for the typical X.Y.Z and # X.Y-Z cases version = version.replace("-", ".") + # version.parse() handles invalid versions by returning LegacyVersion() return packaging.version.parse(version) def _enumerate_versions(self, diff --git a/osv/ecosystems/haskell.py b/osv/ecosystems/haskell.py index 8871a7864fe..569bd21933a 100644 --- a/osv/ecosystems/haskell.py +++ b/osv/ecosystems/haskell.py @@ -40,7 +40,12 @@ def sort_key(self, version): https://hackage.haskell.org/package/Cabal-syntax/docs/Distribution-Types-Version.html """ - return [int(x) for x in version.split('.')] + # If version is not valid, it is most likely an invalid input version + # then sort it to the last/largest element + try: + return [int(x) for x in version.split('.')] + except ValueError: + return [999999] def enumerate_versions(self, package, diff --git a/osv/ecosystems/haskell_test.py b/osv/ecosystems/haskell_test.py index a4e68623bb3..32870edabcc 100644 --- a/osv/ecosystems/haskell_test.py +++ b/osv/ecosystems/haskell_test.py @@ -31,6 +31,10 @@ def test_next_version(self): with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') + def test_sort_key(self): + """Test sort_key.""" + ecosystem = ecosystems.get('Hackage') + self.assertGreater(ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0')) class GHCEcosystemTest(unittest.TestCase): """GHC ecosystem helper tests.""" diff --git a/osv/ecosystems/maven_test.py b/osv/ecosystems/maven_test.py index 5492a0ee6fb..08dd08ab108 100644 --- a/osv/ecosystems/maven_test.py +++ b/osv/ecosystems/maven_test.py @@ -106,6 +106,8 @@ def test_sort(self): '1-a1', '1-alpha-1', '2', + 'invalid', + '0' ] sorted_versions = [ @@ -113,7 +115,7 @@ def test_sort(self): ] self.assertListEqual([ - '1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1', '1', '1', '1', + 'invalid', '0', '1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1', '1', '1', '1', '1', '1', '1.foo', '1-.1', '1-sp', '1-sp', '1-sp-1', '1-sp.1', '1-foo', '1-foo-2', '1-foo-10', '1-1', '1-1', '1-1', '1.1', '1.1', '2' ], sorted_versions) diff --git a/osv/ecosystems/nuget.py b/osv/ecosystems/nuget.py index 86342c485e8..d95b79f46ec 100644 --- a/osv/ecosystems/nuget.py +++ b/osv/ecosystems/nuget.py @@ -72,6 +72,10 @@ def __lt__(self, other): def from_string(cls, str_version): str_version = semver_index.coerce(str_version) str_version, revision = _extract_revision(str_version) + if not semver_index.is_valid(str_version): + # If version is not valid, it is most likely an invalid input + # version then sort it to the last/largest element + return Version(semver_index.parse('999999'), 999999) return Version(semver_index.parse(str_version), revision) diff --git a/osv/ecosystems/nuget_test.py b/osv/ecosystems/nuget_test.py index 21ed0d1adea..992bf03ef59 100644 --- a/osv/ecosystems/nuget_test.py +++ b/osv/ecosystems/nuget_test.py @@ -100,6 +100,11 @@ def test_next_version(self): with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') + def test_sort_key(self): + ecosystem = ecosystems.get('NuGet') + # Tests invalid input versions + self.assertGreater(ecosystem.sort_key('1.4.0rc3'), ecosystem.sort_key('3.0.0.4001')) + if __name__ == '__main__': unittest.main() diff --git a/osv/ecosystems/packagist_test.py b/osv/ecosystems/packagist_test.py index 3a88ace8b04..26b19787bbc 100644 --- a/osv/ecosystems/packagist_test.py +++ b/osv/ecosystems/packagist_test.py @@ -27,6 +27,8 @@ class PackagistEcosystemTest(unittest.TestCase): def test_packagist(self): """Test Packagist.""" ecosystem = ecosystems.get('Packagist') + # Any invalid versions will be handled. + self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0')) self.assertLess( ecosystem.sort_key('4.3-2RC1'), ecosystem.sort_key('4.3-2RC2')) self.assertGreater( diff --git a/osv/ecosystems/pub.py b/osv/ecosystems/pub.py index 7bbbca421ca..01f1fdbebe6 100644 --- a/osv/ecosystems/pub.py +++ b/osv/ecosystems/pub.py @@ -58,7 +58,12 @@ def __eq__(self, other): @classmethod def from_string(cls, str_version): - return Version(semver_index.parse(str_version)) + # If version is not valid, it is most likely an invalid input + # version then sort it to the last/largest element + try: + return Version(semver_index.parse(str_version)) + except ValueError: + return Version(semver_index.parse('999999')) class Pub(Ecosystem): diff --git a/osv/ecosystems/pub_test.py b/osv/ecosystems/pub_test.py index 796540e2c4f..84f20a95e78 100644 --- a/osv/ecosystems/pub_test.py +++ b/osv/ecosystems/pub_test.py @@ -66,6 +66,8 @@ def test_parse(self): pub.Version.from_string('1.2.3+build.1') pub.Version.from_string('1.2.3+x.7.z-92') pub.Version.from_string('1.0.0-rc-1+build-1') + # Tests invalid versions + pub.Version.from_string('3.4.0rc3-invalid') def test_empty_identifier(self): """Test parsing versions with empty identifiers. diff --git a/osv/ecosystems/pypi.py b/osv/ecosystems/pypi.py index ab432651941..f1283b0bd90 100644 --- a/osv/ecosystems/pypi.py +++ b/osv/ecosystems/pypi.py @@ -27,6 +27,7 @@ class PyPI(Ecosystem): def sort_key(self, version): """Sort key.""" + # version.parse() handles invalid versions by returning LegacyVersion() return packaging.version.parse(version) def enumerate_versions(self, diff --git a/osv/ecosystems/pypi_test.py b/osv/ecosystems/pypi_test.py index 3a43f816384..684938716e4 100644 --- a/osv/ecosystems/pypi_test.py +++ b/osv/ecosystems/pypi_test.py @@ -29,3 +29,9 @@ def test_next_version(self): self.assertEqual('0.3.0', ecosystem.next_version('grpcio', '0')) with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') + + def test_sort_key(self): + """Test sort_key""" + ecosystem = ecosystems.get('PyPI') + self.assertGreater(ecosystem.sort_key('2.0.0'), ecosystem.sort_key('1.0.0')) + self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0')) diff --git a/osv/ecosystems/rocky_linux_test.py b/osv/ecosystems/rocky_linux_test.py index 831eb6d9d92..86e41b27cc6 100644 --- a/osv/ecosystems/rocky_linux_test.py +++ b/osv/ecosystems/rocky_linux_test.py @@ -22,13 +22,14 @@ class RockyLinuxEcosystemTest(unittest.TestCase): def test_rocky_linux(self): ecosystem = ecosystems.get('Rocky Linux') - self.assertEqual("Rocky Linux", ecosystem.name) + self.assertEqual('Rocky Linux', ecosystem.name) self.assertGreater( - ecosystem.sort_key("0:0.2.6-20.module+el8.9.0+1420+91577025"), - ecosystem.sort_key("0:0.0.99.4-5.module+el8.9.0+1445+07728297")) + ecosystem.sort_key('0:0.2.6-20.module+el8.9.0+1420+91577025'), + ecosystem.sort_key('0:0.0.99.4-5.module+el8.9.0+1445+07728297')) self.assertGreater( - ecosystem.sort_key("0:0.2.6-20.module+el8.9.0+1420+91577025"), - ecosystem.sort_key("0")) + ecosystem.sort_key('0:0.2.6-20.module+el8.9.0+1420+91577025'), + ecosystem.sort_key('0')) self.assertGreater( - ecosystem.sort_key("2:1.14.3-2.module+el8.10.0+1815+5fe7415e"), - ecosystem.sort_key("2:1.10.3-1.module+el8.10.0+1815+5fe7415e")) + ecosystem.sort_key('2:1.14.3-2.module+el8.10.0+1815+5fe7415e'), + ecosystem.sort_key('2:1.10.3-1.module+el8.10.0+1815+5fe7415e')) + self.assertLess(ecosystem.sort_key('invalid'), ecosystem.sort_key('0')) diff --git a/osv/ecosystems/rubygems.py b/osv/ecosystems/rubygems.py index e76743b8631..4f12eb2b9cf 100644 --- a/osv/ecosystems/rubygems.py +++ b/osv/ecosystems/rubygems.py @@ -15,7 +15,7 @@ import requests -from ..third_party.univers.gem import GemVersion +from ..third_party.univers.gem import GemVersion, InvalidVersionError from . import config from .helper_base import Ecosystem, EnumerateError @@ -28,7 +28,13 @@ class RubyGems(Ecosystem): def sort_key(self, version): """Sort key.""" - return GemVersion(version) + # If version is not valid, it is most likely an invalid input + # version then sort it to the last/largest element + try: + return GemVersion(version) + except InvalidVersionError: + return GemVersion('999999') + def enumerate_versions(self, package, diff --git a/osv/ecosystems/rubygems_test.py b/osv/ecosystems/rubygems_test.py index fee3cd524b1..4ab0b13814e 100644 --- a/osv/ecosystems/rubygems_test.py +++ b/osv/ecosystems/rubygems_test.py @@ -33,3 +33,9 @@ def test_next_version(self): ecosystem.next_version('rails', '5.0.0.beta4')) with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') + + def test_sort_key(self): + """Test sort_key with invalid versions""" + ecosystem = ecosystems.get('RubyGems') + self.assertGreater(ecosystem.sort_key('invalid'), ecosystem.sort_key('4.0.0.rc1')) + self.assertGreater(ecosystem.sort_key('v3.1.1'), ecosystem.sort_key('4.0.0.rc1')) diff --git a/osv/ecosystems/ubuntu_test.py b/osv/ecosystems/ubuntu_test.py index f8c0c1d0171..86dfc865350 100644 --- a/osv/ecosystems/ubuntu_test.py +++ b/osv/ecosystems/ubuntu_test.py @@ -23,11 +23,13 @@ class UbuntuEcosystemTest(unittest.TestCase): def test_ubuntu(self): ecosystem = ecosystems.get('Ubuntu') self.assertGreater( - ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.3"), - ecosystem.sort_key("2.40.0+dfsg-3ubuntu0.5")) + ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.3'), + ecosystem.sort_key('2.40.0+dfsg-3ubuntu0.5')) self.assertGreater( - ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.3"), - ecosystem.sort_key("2.42.8+dfsg-1ubuntu0.2")) - self.assertGreater(ecosystem.sort_key("5.4.13-1"), ecosystem.sort_key("0")) + ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.3'), + ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.2')) + self.assertGreater(ecosystem.sort_key('5.4.13-1'), ecosystem.sort_key('0')) self.assertGreater( - ecosystem.sort_key("5.4.13-1"), ecosystem.sort_key("3.2.30-1")) + ecosystem.sort_key('5.4.13-1'), ecosystem.sort_key('3.2.30-1')) + self.assertGreater( + ecosystem.sort_key('invalid'), ecosystem.sort_key('3.2.30-1')) From 9ce7f5a2d56f6a15b084858ad26a26b1b88ba8dd Mon Sep 17 00:00:00 2001 From: Holly Gong Date: Fri, 23 Aug 2024 12:40:13 +1000 Subject: [PATCH 2/2] fix lint --- osv/ecosystems/alpine_test.py | 4 +-- osv/ecosystems/bioconductor_test.py | 2 +- osv/ecosystems/haskell_test.py | 4 ++- osv/ecosystems/maven_test.py | 39 ++++++----------------------- osv/ecosystems/nuget_test.py | 3 ++- osv/ecosystems/rocky_linux_test.py | 1 + osv/ecosystems/rubygems.py | 1 - osv/ecosystems/rubygems_test.py | 6 +++-- osv/ecosystems/ubuntu_test.py | 1 + 9 files changed, 21 insertions(+), 40 deletions(-) diff --git a/osv/ecosystems/alpine_test.py b/osv/ecosystems/alpine_test.py index 13c316be4bd..2f7aedf34ab 100644 --- a/osv/ecosystems/alpine_test.py +++ b/osv/ecosystems/alpine_test.py @@ -62,9 +62,7 @@ def test_alpine(self, ensure_updated_checkout_mock: mock.MagicMock): ecosystem.sort_key('1.13.2-r0'), ecosystem.sort_key('1.13.2_alpha')) # Check invalid version handle - print(ecosystem.sort_key('1-0-0')) self.assertGreater( - ecosystem.sort_key('1-0-0'), ecosystem.sort_key('1.13.2-r0') - ) + ecosystem.sort_key('1-0-0'), ecosystem.sort_key('1.13.2-r0')) ecosystems.config.set_cache(None) diff --git a/osv/ecosystems/bioconductor_test.py b/osv/ecosystems/bioconductor_test.py index 5aeafa191c2..1b6235a647e 100644 --- a/osv/ecosystems/bioconductor_test.py +++ b/osv/ecosystems/bioconductor_test.py @@ -26,6 +26,6 @@ def test_next_version(self): ecosystem = ecosystems.get('Bioconductor') self.assertEqual('1.18.0', ecosystem.next_version('a4', '1.16.0')) self.assertEqual('1.20.0', ecosystem.next_version('a4', '1.18.0')) - self.assertGreater(ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0')) + self.assertGreater(ecosystem.sort_key('1-0'), ecosystem.sort_key('1.2.0')) with self.assertRaises(ecosystems.EnumerateError): ecosystem.next_version('doesnotexist123456', '1') diff --git a/osv/ecosystems/haskell_test.py b/osv/ecosystems/haskell_test.py index 32870edabcc..9e8ad3137c8 100644 --- a/osv/ecosystems/haskell_test.py +++ b/osv/ecosystems/haskell_test.py @@ -34,7 +34,9 @@ def test_next_version(self): def test_sort_key(self): """Test sort_key.""" ecosystem = ecosystems.get('Hackage') - self.assertGreater(ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0')) + self.assertGreater( + ecosystem.sort_key('1-20-0'), ecosystem.sort_key('1.20.0')) + class GHCEcosystemTest(unittest.TestCase): """GHC ecosystem helper tests.""" diff --git a/osv/ecosystems/maven_test.py b/osv/ecosystems/maven_test.py index 08dd08ab108..6de7b8e4336 100644 --- a/osv/ecosystems/maven_test.py +++ b/osv/ecosystems/maven_test.py @@ -80,34 +80,10 @@ def test_sort(self): """Basic sort tests.""" # These tests are taken from the spec. unsorted = [ - '1', - '1.1', - '1-snapshot', - '1', - '1-sp', - '1-foo2', - '1-foo10', - '1.foo', - '1-foo', - '1-1', - '1.1', - '1.ga', - '1-ga', - '1-0', - '1.0', - '1', - '1-sp', - '1-ga', - '1-sp.1', - '1-ga.1', - '1-sp-1', - '1-ga-1', - '1-1', - '1-a1', - '1-alpha-1', - '2', - 'invalid', - '0' + '1', '1.1', '1-snapshot', '1', '1-sp', '1-foo2', '1-foo10', '1.foo', + '1-foo', '1-1', '1.1', '1.ga', '1-ga', '1-0', '1.0', '1', '1-sp', + '1-ga', '1-sp.1', '1-ga.1', '1-sp-1', '1-ga-1', '1-1', '1-a1', + '1-alpha-1', '2', 'invalid', '0' ] sorted_versions = [ @@ -115,9 +91,10 @@ def test_sort(self): ] self.assertListEqual([ - 'invalid', '0', '1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1', '1', '1', '1', - '1', '1', '1.foo', '1-.1', '1-sp', '1-sp', '1-sp-1', '1-sp.1', '1-foo', - '1-foo-2', '1-foo-10', '1-1', '1-1', '1-1', '1.1', '1.1', '2' + 'invalid', '0', '1-alpha-1', '1-alpha-1', '1-snapshot', '1', '1', '1', + '1', '1', '1', '1', '1', '1.foo', '1-.1', '1-sp', '1-sp', '1-sp-1', + '1-sp.1', '1-foo', '1-foo-2', '1-foo-10', '1-1', '1-1', '1-1', '1.1', + '1.1', '2' ], sorted_versions) def test_versions_qualifiers(self): diff --git a/osv/ecosystems/nuget_test.py b/osv/ecosystems/nuget_test.py index 992bf03ef59..bd4be2b3f6f 100644 --- a/osv/ecosystems/nuget_test.py +++ b/osv/ecosystems/nuget_test.py @@ -103,7 +103,8 @@ def test_next_version(self): def test_sort_key(self): ecosystem = ecosystems.get('NuGet') # Tests invalid input versions - self.assertGreater(ecosystem.sort_key('1.4.0rc3'), ecosystem.sort_key('3.0.0.4001')) + self.assertGreater( + ecosystem.sort_key('1.4.0rc3'), ecosystem.sort_key('3.0.0.4001')) if __name__ == '__main__': diff --git a/osv/ecosystems/rocky_linux_test.py b/osv/ecosystems/rocky_linux_test.py index 86e41b27cc6..b54c9f95c36 100644 --- a/osv/ecosystems/rocky_linux_test.py +++ b/osv/ecosystems/rocky_linux_test.py @@ -21,6 +21,7 @@ class RockyLinuxEcosystemTest(unittest.TestCase): """Rocky Linux ecosystem helper tests.""" def test_rocky_linux(self): + """Test sort_key""" ecosystem = ecosystems.get('Rocky Linux') self.assertEqual('Rocky Linux', ecosystem.name) self.assertGreater( diff --git a/osv/ecosystems/rubygems.py b/osv/ecosystems/rubygems.py index 4f12eb2b9cf..38e528917b4 100644 --- a/osv/ecosystems/rubygems.py +++ b/osv/ecosystems/rubygems.py @@ -35,7 +35,6 @@ def sort_key(self, version): except InvalidVersionError: return GemVersion('999999') - def enumerate_versions(self, package, introduced, diff --git a/osv/ecosystems/rubygems_test.py b/osv/ecosystems/rubygems_test.py index 4ab0b13814e..22ddad307ad 100644 --- a/osv/ecosystems/rubygems_test.py +++ b/osv/ecosystems/rubygems_test.py @@ -37,5 +37,7 @@ def test_next_version(self): def test_sort_key(self): """Test sort_key with invalid versions""" ecosystem = ecosystems.get('RubyGems') - self.assertGreater(ecosystem.sort_key('invalid'), ecosystem.sort_key('4.0.0.rc1')) - self.assertGreater(ecosystem.sort_key('v3.1.1'), ecosystem.sort_key('4.0.0.rc1')) + self.assertGreater( + ecosystem.sort_key('invalid'), ecosystem.sort_key('4.0.0.rc1')) + self.assertGreater( + ecosystem.sort_key('v3.1.1'), ecosystem.sort_key('4.0.0.rc1')) diff --git a/osv/ecosystems/ubuntu_test.py b/osv/ecosystems/ubuntu_test.py index 86dfc865350..8224319e1eb 100644 --- a/osv/ecosystems/ubuntu_test.py +++ b/osv/ecosystems/ubuntu_test.py @@ -21,6 +21,7 @@ class UbuntuEcosystemTest(unittest.TestCase): """Ubuntu ecosystem helper tests.""" def test_ubuntu(self): + """Test sort_key""" ecosystem = ecosystems.get('Ubuntu') self.assertGreater( ecosystem.sort_key('2.42.8+dfsg-1ubuntu0.3'),