Add channel-aware release tooling and updater recovery
This commit is contained in:
172
release_tests/test_update_resolver.py
Normal file
172
release_tests/test_update_resolver.py
Normal file
@@ -0,0 +1,172 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user