Add channel-aware release tooling and updater recovery
This commit is contained in:
156
release_tests/test_release_history.py
Normal file
156
release_tests/test_release_history.py
Normal file
@@ -0,0 +1,156 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from scripts.generate_history import load_release_statuses, release_history
|
||||
from scripts.release_lib import ReleaseToolError
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
|
||||
def gitea_release(tag: str, *, prerelease: bool) -> dict[str, object]:
|
||||
return {
|
||||
"id": 42,
|
||||
"tag_name": tag,
|
||||
"name": f"Release {tag}",
|
||||
"draft": False,
|
||||
"prerelease": prerelease,
|
||||
"published_at": "2026-09-08T12:00:00Z",
|
||||
"html_url": f"https://gitea.example.test/owner/project/releases/tag/{tag}",
|
||||
"assets": [
|
||||
{
|
||||
"name": "manifest.json",
|
||||
"browser_download_url": (
|
||||
f"https://gitea.example.test/owner/project/releases/download/"
|
||||
f"{tag}/manifest.json"
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "manifest.json.sig",
|
||||
"browser_download_url": (
|
||||
f"https://gitea.example.test/owner/project/releases/download/"
|
||||
f"{tag}/manifest.json.sig"
|
||||
),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class ReleaseHistoryTests(unittest.TestCase):
|
||||
def test_history_uses_only_installable_immutable_releases(self) -> None:
|
||||
stable = gitea_release("v1.8.0", prerelease=False)
|
||||
beta = gitea_release("v1.9.0-beta.2", prerelease=True)
|
||||
wrong_flag = gitea_release("v2.0.0-beta.1", prerelease=False)
|
||||
draft = gitea_release("v2.0.0", prerelease=False)
|
||||
draft["draft"] = True
|
||||
|
||||
history = release_history(
|
||||
[stable, wrong_flag, draft, beta],
|
||||
"2026-09-08T12:01:00Z",
|
||||
)
|
||||
|
||||
releases = history["releases"]
|
||||
assert isinstance(releases, list)
|
||||
self.assertEqual(
|
||||
[release["version"] for release in releases],
|
||||
["1.9.0-beta.2", "1.8.0"],
|
||||
)
|
||||
self.assertEqual(releases[0]["channel"], "prerelease")
|
||||
self.assertEqual(releases[1]["channel"], "stable")
|
||||
self.assertTrue(releases[0]["manifest_url"].endswith("/manifest.json"))
|
||||
|
||||
def test_history_rejects_an_invalid_generation_timestamp(self) -> None:
|
||||
with self.assertRaisesRegex(ReleaseToolError, "timestamp"):
|
||||
release_history([], "today")
|
||||
|
||||
def test_history_applies_signed_release_status_overrides(self) -> None:
|
||||
history = release_history(
|
||||
[gitea_release("v1.9.0-beta.2", prerelease=True)],
|
||||
"2026-09-08T12:01:00Z",
|
||||
{"v1.9.0-beta.2": "withdrawn"},
|
||||
)
|
||||
|
||||
releases = history["releases"]
|
||||
assert isinstance(releases, list)
|
||||
self.assertEqual(releases[0]["status"], "withdrawn")
|
||||
self.assertEqual(
|
||||
releases[0]["compatibility"],
|
||||
{"config_schema": 1, "minimum_config_schema": 1},
|
||||
)
|
||||
|
||||
def test_history_supports_explicit_config_compatibility(self) -> None:
|
||||
history = release_history(
|
||||
[gitea_release("v2.0.0", prerelease=False)],
|
||||
"2026-09-08T12:01:00Z",
|
||||
{
|
||||
"v2.0.0": {
|
||||
"status": "active",
|
||||
"config_schema": 3,
|
||||
"minimum_config_schema": 2,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
releases = history["releases"]
|
||||
assert isinstance(releases, list)
|
||||
self.assertEqual(
|
||||
releases[0]["compatibility"],
|
||||
{"config_schema": 3, "minimum_config_schema": 2},
|
||||
)
|
||||
|
||||
def test_status_file_rejects_unknown_fields_and_unsafe_schema_ranges(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
path = Path(temporary) / "statuses.json"
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema": 1,
|
||||
"releases": {
|
||||
"v2.0.0": {
|
||||
"status": "active",
|
||||
"config_schema": 1,
|
||||
"minimum_config_schema": 2,
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
with self.assertRaisesRegex(ReleaseToolError, "exceeds"):
|
||||
load_release_statuses(path)
|
||||
path.write_text(
|
||||
'{"schema":1,"releases":{"v2.0.0":{"status":"active","note":"x"}}}',
|
||||
encoding="utf-8",
|
||||
)
|
||||
with self.assertRaisesRegex(ReleaseToolError, "unknown"):
|
||||
load_release_statuses(path)
|
||||
|
||||
def test_history_rejects_an_unknown_status(self) -> None:
|
||||
with self.assertRaisesRegex(ReleaseToolError, "invalid status"):
|
||||
release_history(
|
||||
[gitea_release("v1.8.0", prerelease=False)],
|
||||
"2026-09-08T12:01:00Z",
|
||||
{"v1.8.0": "broken"},
|
||||
)
|
||||
|
||||
def test_history_rejects_status_override_for_a_missing_release(self) -> None:
|
||||
with self.assertRaisesRegex(ReleaseToolError, "do not match"):
|
||||
release_history(
|
||||
[gitea_release("v1.8.0", prerelease=False)],
|
||||
"2026-09-08T12:01:00Z",
|
||||
{"v1.9.0-beta.1": "withdrawn"},
|
||||
)
|
||||
|
||||
def test_history_rejects_duplicate_release_tags(self) -> None:
|
||||
release = gitea_release("v1.8.0", prerelease=False)
|
||||
with self.assertRaisesRegex(ReleaseToolError, "duplicate"):
|
||||
release_history(
|
||||
[release, dict(release)],
|
||||
"2026-09-08T12:01:00Z",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user