from __future__ import annotations import unittest from ptlinux.update_resolver import ( BLOCKED_STATUS, PRERELEASE_CHANNEL, STABLE_CHANNEL, WITHDRAWN_STATUS, ReleaseModelError, ReleaseRecord, SemanticVersion, UpdateResolver, previous_release_tag, validate_channel_version, validate_release_progression, ) class SemanticVersionTests(unittest.TestCase): def test_orders_beta_rc_final_and_multidigit_versions(self) -> None: versions = [ SemanticVersion.parse(value) for value in ( "1.9.0", "1.9.0-rc.1", "1.10.0", "1.9.0-beta.2", "1.9.0-beta.1", "1.9.9", ) ] self.assertEqual( [str(value) for value in sorted(versions)], [ "1.9.0-beta.1", "1.9.0-beta.2", "1.9.0-rc.1", "1.9.0", "1.9.9", "1.10.0", ], ) def test_rejects_unsupported_or_noncanonical_versions(self) -> None: for value in ( "v1.2.3", "01.2.3", "1.02.3", "1.2.03", "1.2.3-alpha.1", "1.2.3-beta", "1.2.3+build", ): with self.subTest(value=value): with self.assertRaises(ReleaseModelError): SemanticVersion.parse(value) def test_rejects_channel_version_mismatches(self) -> None: with self.assertRaisesRegex(ReleaseModelError, "belongs to prerelease"): validate_channel_version(STABLE_CHANNEL, "1.9.0-beta.1") with self.assertRaisesRegex(ReleaseModelError, "belongs to stable"): validate_channel_version(PRERELEASE_CHANNEL, "1.9.0") def test_selects_channel_specific_changelog_bases(self) -> None: tags = ["not-a-release", "v1.7.4", "v1.9.0-beta.1", "v1.9.0-beta.2"] self.assertEqual( previous_release_tag(STABLE_CHANNEL, "1.8.0", tags), "v1.7.4", ) self.assertEqual( previous_release_tag(PRERELEASE_CHANNEL, "1.9.0-rc.1", tags), "v1.9.0-beta.2", ) self.assertEqual( previous_release_tag(PRERELEASE_CHANNEL, "2.0.0-beta.1", tags), "v1.7.4", ) def test_release_progression_cannot_move_a_channel_backwards(self) -> None: validate_release_progression( STABLE_CHANNEL, "1.9.0", ["v1.8.0", "v2.0.0-beta.1"], ) validate_release_progression( PRERELEASE_CHANNEL, "2.0.0-beta.2", ["v1.9.0", "v2.0.0-beta.1"], ) with self.assertRaisesRegex(ReleaseModelError, "newer"): validate_release_progression( STABLE_CHANNEL, "1.8.0", ["v1.9.0"], ) with self.assertRaisesRegex(ReleaseModelError, "newer"): validate_release_progression( PRERELEASE_CHANNEL, "1.9.0-rc.1", ["v2.0.0-beta.1"], ) class UpdateResolverTests(unittest.TestCase): def setUp(self) -> None: self.resolver = UpdateResolver( [ ReleaseRecord("1.7.4", STABLE_CHANNEL, tag="v1.7.4"), ReleaseRecord("1.8.0", STABLE_CHANNEL, tag="v1.8.0"), ReleaseRecord("1.9.0-beta.1", PRERELEASE_CHANNEL), ReleaseRecord("1.9.0-beta.2", PRERELEASE_CHANNEL), ReleaseRecord("1.9.0-rc.1", PRERELEASE_CHANNEL), ReleaseRecord("2.0.0-beta.1", PRERELEASE_CHANNEL, WITHDRAWN_STATUS), ReleaseRecord("2.0.0-beta.2", PRERELEASE_CHANNEL, BLOCKED_STATUS), ] ) def test_stable_clients_never_receive_prereleases(self) -> None: candidate = self.resolver.update_candidate("1.7.4", STABLE_CHANNEL) self.assertIsNotNone(candidate) assert candidate is not None self.assertEqual(candidate.version, "1.8.0") def test_prerelease_clients_receive_highest_active_semantic_version(self) -> None: candidate = self.resolver.update_candidate( "1.9.0-beta.1", PRERELEASE_CHANNEL, ) self.assertIsNotNone(candidate) assert candidate is not None self.assertEqual(candidate.version, "1.9.0-rc.1") def test_prerelease_client_transitions_to_the_final_stable_release(self) -> None: resolver = UpdateResolver( [ ReleaseRecord("1.9.0-rc.1", PRERELEASE_CHANNEL), ReleaseRecord("1.9.0", STABLE_CHANNEL), ] ) candidate = resolver.update_candidate("1.9.0-rc.1", PRERELEASE_CHANNEL) self.assertIsNotNone(candidate) assert candidate is not None self.assertEqual(candidate.version, "1.9.0") def test_switching_to_stable_does_not_downgrade(self) -> None: self.assertIsNone( self.resolver.update_candidate("1.9.0-beta.2", STABLE_CHANNEL) ) def test_channel_aware_rollback_candidates_are_real_releases(self) -> None: stable = self.resolver.rollback_candidates("1.9.0", STABLE_CHANNEL) prerelease = self.resolver.rollback_candidates( "1.9.0-rc.1", PRERELEASE_CHANNEL, ) self.assertEqual([item.version for item in stable], ["1.8.0", "1.7.4"]) self.assertEqual( [item.version for item in prerelease], ["1.9.0-beta.2", "1.9.0-beta.1", "1.8.0", "1.7.4"], ) if __name__ == "__main__": unittest.main()