mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-15 00:09:39 +08:00
155 lines
8.3 KiB
Python
155 lines
8.3 KiB
Python
"""Regression coverage for independent and combined release metadata."""
|
|
|
|
import copy
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
import update_version_manifest as manifest
|
|
|
|
|
|
class ManifestTest(unittest.TestCase):
|
|
def setUp(self):
|
|
temporary = tempfile.TemporaryDirectory(prefix="crossdesk-manifest-")
|
|
self.addCleanup(temporary.cleanup)
|
|
self.root = Path(temporary.name)
|
|
self.artifacts = self.root / "release"
|
|
self.artifacts.mkdir()
|
|
self.current = {
|
|
"version": "1.6.0-20260930", "latest_version": "1.6.0-20260930",
|
|
"releaseDate": "2026-09-30", "releaseNotes": "Desktop notes", "patch": 0,
|
|
"downloads": {"windows-x64": {"url": "https://downloads.crossdesk.cn/existing.exe"}},
|
|
"custom": {"keep": True},
|
|
}
|
|
|
|
def files(self, *names):
|
|
for path in self.artifacts.iterdir():
|
|
path.unlink()
|
|
for name in names:
|
|
(self.artifacts / name).write_bytes(b"release fixture")
|
|
return manifest.artifact_downloads(self.artifacts)
|
|
|
|
def merge(self, tag, downloads, current=None):
|
|
return manifest.merge_release(current or self.current, tag, tag, "Release notes", downloads)
|
|
|
|
def test_mobile_preserves_desktop_metadata_and_downloads(self):
|
|
downloads = self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
original = copy.deepcopy(self.current)
|
|
result = self.merge("android-v1.6.0-20261002", downloads)
|
|
for key, value in original.items():
|
|
if key != "downloads":
|
|
self.assertEqual(result[key], value)
|
|
self.assertEqual(result["downloads"]["windows-x64"], original["downloads"]["windows-x64"])
|
|
self.assertEqual(result["downloads"]["android-arm64"]["url"],
|
|
"https://downloads.crossdesk.cn/crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
self.assertEqual(result["downloads"]["android-arm64"]["version"], "1.6.0-20261002")
|
|
self.assertEqual(result["downloads"]["android-arm64"]["releaseDate"], "2026-10-02")
|
|
self.assertNotIn("platforms", result)
|
|
self.assertEqual(self.current, original)
|
|
|
|
def test_sequential_platform_releases_retain_each_other(self):
|
|
for order in (("ios", "android"), ("android", "ios")):
|
|
result = copy.deepcopy(self.current)
|
|
for platform in order:
|
|
version, extension = ("0.0.2", "zip") if platform == "ios" else ("1.6.0", "apk")
|
|
downloads = self.files(f"crossdesk-{platform}-arm64-unsigned-v{version}-20261002.{extension}")
|
|
result = self.merge(f"{platform}-v{version}-20261002", downloads, result)
|
|
self.assertNotIn("platforms", result)
|
|
self.assertEqual(result["version"], self.current["version"])
|
|
self.assertTrue({"windows-x64", "ios-arm64", "android-arm64"} <= result["downloads"].keys())
|
|
self.assertEqual(result["downloads"]["ios-arm64"]["version"], "0.0.2-20261002")
|
|
self.assertEqual(result["downloads"]["android-arm64"]["version"], "1.6.0-20261002")
|
|
|
|
def test_combined_release_records_independent_mobile_versions(self):
|
|
downloads = self.files("crossdesk-win-x64-v1.6.1-20261002.exe",
|
|
"crossdesk-win-x64-portable-v1.6.1-20261002.zip",
|
|
"crossdesk-ios-arm64-unsigned-v0.0.2-20261002.zip",
|
|
"crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
result = self.merge("v1.6.1-20261002", downloads)
|
|
self.assertEqual(result["version"], "1.6.1-20261002")
|
|
self.assertEqual(result["downloads"]["ios-arm64"]["version"], "0.0.2-20261002")
|
|
self.assertEqual(result["downloads"]["android-arm64"]["version"], "1.6.0-20261002")
|
|
self.assertNotIn("platforms", result)
|
|
|
|
def test_obsolete_platforms_are_removed_on_any_release(self):
|
|
for tag, artifact in (
|
|
("android-v1.6.0-20261002", "crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk"),
|
|
("v1.6.1-20261002", "crossdesk-win-x64-v1.6.1-20261002.exe"),
|
|
):
|
|
downloads = self.files(artifact)
|
|
expected = self.merge(tag, downloads)
|
|
for obsolete in ({"ios": {"version": "0.0.1"}}, []):
|
|
current = {**copy.deepcopy(self.current), "platforms": obsolete}
|
|
original = copy.deepcopy(current)
|
|
self.assertEqual(self.merge(tag, downloads, current), expected)
|
|
self.assertEqual(current, original)
|
|
|
|
def test_mobile_tag_must_match_artifact_and_platform(self):
|
|
downloads = self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
for tag in ("android-v1.7.0-20261002", "ios-v1.6.0-20261002"):
|
|
with self.assertRaises(ValueError):
|
|
self.merge(tag, downloads)
|
|
|
|
def test_signed_android_release_updates_existing_unsigned_download(self):
|
|
unsigned = self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
current = self.merge("android-v1.6.0-20261002", unsigned)
|
|
signed = self.files("crossdesk-android-arm64-v1.6.0-20261002.apk")
|
|
result = self.merge("android-v1.6.0-20261002", signed, current)
|
|
self.assertEqual(result["downloads"]["android-arm64"]["url"],
|
|
"https://downloads.crossdesk.cn/crossdesk-android-arm64-v1.6.0-20261002.apk")
|
|
self.assertEqual(result["version"], self.current["version"])
|
|
self.assertEqual(result["downloads"]["windows-x64"], self.current["downloads"]["windows-x64"])
|
|
self.assertNotIn("platforms", result)
|
|
|
|
def test_signed_android_is_preferred_when_release_retains_unsigned_apk(self):
|
|
downloads = self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk",
|
|
"crossdesk-android-arm64-v1.6.0-20261002.apk")
|
|
self.assertEqual(downloads["android-arm64"]["filename"],
|
|
"crossdesk-android-arm64-v1.6.0-20261002.apk")
|
|
with self.assertRaises(ValueError):
|
|
self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261001.apk",
|
|
"crossdesk-android-arm64-v1.6.0-20261002.apk")
|
|
|
|
def test_invalid_current_manifest_is_rejected(self):
|
|
downloads = self.files("crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")
|
|
for current in ([], {}, {"downloads": []}):
|
|
with self.assertRaises(ValueError):
|
|
manifest.merge_release(current, "android-v1.6.0-20261002", "Android", "", downloads)
|
|
|
|
def test_missing_duplicate_and_invalid_artifacts_are_rejected(self):
|
|
for names in ((), ("crossdesk-android-arm64-unsigned-v1.6.0-20260230.apk",),
|
|
("crossdesk-android-arm64-unsigned-v1.6.0-20261001.apk",
|
|
"crossdesk-android-arm64-unsigned-v1.6.0-20261002.apk")):
|
|
with self.assertRaises(ValueError):
|
|
self.files(*names)
|
|
|
|
def test_hotfix_version_and_date_formats(self):
|
|
for value in ("v1.6.0-2-20261002", "v1.6.0-20261002-2"):
|
|
info = manifest.version_info(value)
|
|
self.assertEqual(info["patch"], 2)
|
|
self.assertEqual(info["releaseDate"], "2026-10-02")
|
|
|
|
def test_cli_preserves_literal_notes_and_release_metadata(self):
|
|
self.files("crossdesk-win-x64-v1.6.1-20261002.exe")
|
|
notes = '中文 release notes\n"quoted" $(not-a-command) $HOME `literal`'
|
|
release = {"tagName": "v1.6.1-20261002", "name": "Desktop release", "body": notes}
|
|
(self.root / "current.json").write_text(json.dumps(self.current))
|
|
(self.root / "release.json").write_text(json.dumps(release))
|
|
output = self.root / "version.json"
|
|
subprocess.run([sys.executable, str(Path(manifest.__file__)),
|
|
"--current", str(self.root / "current.json"), "--output", str(output),
|
|
"--artifacts", str(self.artifacts), "--release-json", str(self.root / "release.json")],
|
|
check=True, capture_output=True, text=True)
|
|
result = json.loads(output.read_text())
|
|
self.assertEqual(result["releaseNotes"], notes)
|
|
self.assertEqual(result["releaseName"], "Desktop release")
|
|
self.assertEqual(result["tagName"], "v1.6.1-20261002")
|
|
self.assertNotIn("platforms", result)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|