Files
simplex-chat/scripts/ci/test_packages_release.py
T
2026-10-12 14:05:48 +00:00

241 lines
9.8 KiB
Python

import subprocess
import tempfile
import unittest
from dataclasses import replace
from pathlib import Path
from unittest import mock
import packages_release
from packages_release import (
ADDON_ASSETS,
LIBS_ASSETS,
Package,
Pins,
State,
addon_tag,
bump_npm_version,
bump_python_version,
changed_since_publish,
published_tag,
dist_tag,
parse_python_version,
parse_release,
plan,
registry_name,
release_digests,
render_python_version,
render_release,
)
def sha(n: int) -> str:
return f"{n:x}" * 64
def digests(assets: list[str], n: int) -> dict[str, str]:
return {asset: sha(n) for asset in assets}
def github_release(assets: list[str], n: int) -> dict:
return {"assets": [{"name": name, "digest": f"sha256:{sha(n)}"} for name in assets]}
def state(**overrides) -> State:
current = State(
libs_tag="core-1.0.0.1",
libs_digests=digests(LIBS_ASSETS, 1),
addon_tag="nodejs-addon-aaa",
addon_digests=digests(ADDON_ASSETS, 2),
nodejs_pins=Pins("core-1.0.0.1", {**digests(LIBS_ASSETS, 1), **digests(ADDON_ASSETS, 2)}, "nodejs-addon-aaa"),
python_pins=Pins("core-1.0.0.1", digests(LIBS_ASSETS, 1)),
types=Package("0.12.1", changed=False, published=True),
nodejs=Package("7.1.0-beta.6.1", changed=False, published=True),
python=Package("7.1.0b6", changed=False, published=True),
)
return replace(current, **overrides)
NEW_LIBS = {"libs_tag": "core-1.0.0.2", "libs_digests": digests(LIBS_ASSETS, 3)}
NEW_ADDON = {"addon_tag": "nodejs-addon-bbb", "addon_digests": digests(ADDON_ASSETS, 4)}
class VersionTest(unittest.TestCase):
def test_npm_bump_adds_a_package_release_to_an_app_version(self):
self.assertEqual(bump_npm_version("7.1.0-beta.6"), "7.1.0-beta.6.1")
self.assertEqual(bump_npm_version("7.1.0-beta.6.1"), "7.1.0-beta.6.2")
self.assertEqual(bump_npm_version("0.12.9"), "0.12.10")
self.assertEqual(bump_npm_version("7.0.2"), "7.0.3")
def test_python_bump_uses_post_releases(self):
self.assertEqual(bump_python_version("7.1.0b6"), "7.1.0b6.post1")
self.assertEqual(bump_python_version("7.1.0b6.post9"), "7.1.0b6.post10")
self.assertEqual(bump_python_version("7.0.2"), "7.0.2.post1")
def test_prereleases_publish_as_beta(self):
self.assertEqual(dist_tag("7.1.0-beta.6.2"), "beta")
self.assertEqual(dist_tag("7.1.0"), "latest")
def test_test_scope_renames_npm_packages(self):
self.assertEqual(registry_name("@simplex-chat/types", "@test"), "@test/types")
self.assertEqual(registry_name("simplex-chat", "@test"), "@test/simplex-chat")
self.assertEqual(registry_name("simplex-chat", None), "simplex-chat")
class AddonTagTest(unittest.TestCase):
def test_depends_on_sources_and_node_addon_api(self):
files = {"a.cc": b"1", "b.h": b"2"}
tag = addon_tag(files, "^8.5.0")
self.assertRegex(tag, r"^nodejs-addon-[0-9a-f]{12}$")
self.assertEqual(addon_tag(dict(reversed(files.items())), "^8.5.0"), tag)
self.assertNotEqual(addon_tag({**files, "b.h": b"3"}, "^8.5.0"), tag)
self.assertNotEqual(addon_tag({"a.cc": b"1", "c.h": b"2"}, "^8.5.0"), tag)
self.assertNotEqual(addon_tag(files, "^8.6.0"), tag)
class PinFilesTest(unittest.TestCase):
def test_release_ts_round_trip(self):
pins = Pins("core-1.0.0.1", {"b.zip": sha(2), "a.zip": sha(1)}, "nodejs-addon-aaa")
self.assertEqual(parse_release(render_release(pins)), pins)
self.assertIsNone(parse_release(render_release(replace(pins, addon_release=None))).addon_release)
def test_python_version_round_trip(self):
pins = Pins("core-1.0.0.1", {"b.zip": sha(2), "a.zip": sha(1)})
self.assertEqual(parse_python_version(render_python_version("7.1.0b6.post1", pins)), ("7.1.0b6.post1", pins))
class ReleaseDigestsTest(unittest.TestCase):
def test_requires_every_asset_with_a_digest(self):
self.assertEqual(release_digests(github_release(LIBS_ASSETS, 3), LIBS_ASSETS), digests(LIBS_ASSETS, 3))
self.assertIsNone(release_digests(github_release(LIBS_ASSETS[1:], 3), LIBS_ASSETS))
self.assertIsNone(release_digests(None, LIBS_ASSETS))
no_digests = {"assets": [{"name": name, "digest": None} for name in LIBS_ASSETS]}
self.assertIsNone(release_digests(no_digests, LIBS_ASSETS))
class PlanTest(unittest.TestCase):
def test_nothing_changes_when_current(self):
result = plan(state())
self.assertEqual((result.nodejs_changed, result.python_changed, result.types_bumped), (False, False, False))
self.assertEqual(result.notes, [])
def test_new_libs_release_updates_both_libraries(self):
result = plan(state(**NEW_LIBS))
self.assertEqual(result.nodejs_pins.libs_release, "core-1.0.0.2")
self.assertEqual(result.python_pins.libs_release, "core-1.0.0.2")
self.assertEqual(result.nodejs_pins.sha256[LIBS_ASSETS[0]], sha(3))
self.assertEqual(result.nodejs_pins.sha256[ADDON_ASSETS[0]], sha(2))
self.assertEqual(result.python_pins.sha256[LIBS_ASSETS[0]], sha(3))
self.assertEqual(result.title, "packages: release nodejs 7.1.0-beta.6.2, python 7.1.0b6.post1")
def test_new_addon_release_updates_nodejs_only(self):
result = plan(state(**NEW_ADDON))
self.assertEqual(result.nodejs_pins.addon_release, "nodejs-addon-bbb")
self.assertEqual(result.nodejs_pins.sha256[ADDON_ASSETS[0]], sha(4))
self.assertEqual(result.title, "packages: release nodejs 7.1.0-beta.6.2")
def test_incomplete_release_keeps_the_pins(self):
result = plan(state(addon_tag="nodejs-addon-bbb", addon_digests=None))
self.assertEqual(result.nodejs_pins.addon_release, "nodejs-addon-aaa")
self.assertFalse(result.nodejs_changed)
self.assertEqual(
result.notes,
["Node.js add-on release nodejs-addon-bbb is missing or incomplete; nodejs-addon-aaa stays pinned"],
)
def test_source_changes_bump_their_package(self):
self.assertEqual(
plan(state(nodejs=Package("7.1.0-beta.6.1", True, True))).title, "packages: release nodejs 7.1.0-beta.6.2"
)
self.assertEqual(
plan(state(python=Package("7.1.0b6", True, True))).title, "packages: release python 7.1.0b6.post1"
)
def test_types_bump_updates_the_nodejs_dependency(self):
result = plan(state(types=Package("0.12.1", True, True)))
self.assertEqual(result.types_dependency, "^0.12.2")
self.assertEqual(result.title, "packages: release types 0.12.2, nodejs 7.1.0-beta.6.2")
def test_all_changes_combine_into_one_release(self):
result = plan(
state(
**NEW_LIBS,
**NEW_ADDON,
types=Package("0.12.1", True, True),
nodejs=Package("7.1.0-beta.6.1", True, True),
python=Package("7.1.0b6", True, True),
)
)
self.assertEqual(
(result.nodejs_pins.libs_release, result.nodejs_pins.addon_release), ("core-1.0.0.2", "nodejs-addon-bbb")
)
self.assertEqual(result.title, "packages: release types 0.12.2, nodejs 7.1.0-beta.6.2, python 7.1.0b6.post1")
def test_unpublished_versions_are_not_bumped_again(self):
result = plan(
state(
**NEW_LIBS, nodejs=Package("7.1.0-beta.6.2", True, False), python=Package("7.1.0b6.post1", False, False)
)
)
self.assertEqual(result.title, "packages: release nodejs 7.1.0-beta.6.2, python 7.1.0b6.post1")
def test_requested_versions_win(self):
result = plan(state(nodejs_version="7.2.0", types_version="0.13.0", python_version="7.2.0"))
self.assertEqual(result.types_dependency, "^0.13.0")
self.assertEqual(result.title, "packages: release types 0.13.0, nodejs 7.2.0, python 7.2.0")
class ChangedSincePublishTest(unittest.TestCase):
def setUp(self):
self.repo = Path(tempfile.mkdtemp())
self.git("init", "-q")
self.write("pkg/version.txt", 'version = "1"\n')
self.write("pkg/src.txt", "a")
self.commit("set version")
def git(self, *args):
subprocess.run(["git", "-c", "user.name=t", "-c", "user.email=t@t", *args], cwd=self.repo, check=True)
def write(self, file, text):
(self.repo / file).parent.mkdir(parents=True, exist_ok=True)
(self.repo / file).write_text(text)
def commit(self, message):
self.git("add", "-A")
self.git("commit", "-q", "-m", message)
def changed(self):
with mock.patch.object(packages_release, "ROOT", self.repo):
return changed_since_publish("pkg", "1", "pkg", "pkg/version.txt", "^version =")
def test_compares_with_the_commit_that_set_the_version(self):
self.assertFalse(self.changed())
self.write("pkg/src.txt", "b")
self.commit("change")
self.assertTrue(self.changed())
def test_compares_with_the_published_commit(self):
self.write("pkg/src.txt", "b")
self.commit("change")
self.git("tag", "pkg-v1")
self.assertFalse(self.changed())
self.write("pkg/src.txt", "c")
self.commit("change after publish")
self.assertTrue(self.changed())
def test_publish_tag_marks_the_version_published(self):
with mock.patch.object(packages_release, "ROOT", self.repo):
self.assertIsNone(published_tag("pkg", "1"))
self.git("tag", "pkg-v1")
self.assertEqual(published_tag("pkg", "1"), "refs/tags/pkg-v1")
self.assertIsNone(published_tag("pkg", "2"))
def test_ignores_tests_and_other_directories(self):
self.write("pkg/tests/t.txt", "t")
self.write("other/file.txt", "o")
self.commit("tests and other files")
self.assertFalse(self.changed())
if __name__ == "__main__":
unittest.main()