99 lines
3.9 KiB
Python
99 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from bot_bottle.release_bundle import build_bundle_index, canonical_bytes
|
|
from bot_bottle.release_manifest import ReleaseManifestError
|
|
from bot_bottle.release_publish import bundle_url, publish_bundle
|
|
from tests.unit.test_release_manifest import manifest
|
|
|
|
|
|
class TestReleasePublish(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
env = {
|
|
"BOT_BOTTLE_RELEASE_BASE": "https://packages.example",
|
|
"BOT_BOTTLE_RELEASE_OWNER": "owner",
|
|
"BOT_BOTTLE_RELEASE_TOKEN": "token",
|
|
}
|
|
self.env = patch.dict("os.environ", env, clear=True)
|
|
self.env.start()
|
|
self.addCleanup(self.env.stop)
|
|
|
|
def bundle(self, root: Path) -> tuple[dict[str, object], Path]:
|
|
wheel = root / "bot_bottle-0.1.0-py3-none-any.whl"
|
|
wheel.write_bytes(b"wheel")
|
|
index = build_bundle_index(
|
|
manifest(),
|
|
wheel=wheel,
|
|
wheel_url=bundle_url("1" * 40, wheel.name),
|
|
workflow_run="actions/123",
|
|
published_at="2026-07-27T00:00:00Z",
|
|
)
|
|
return index, wheel
|
|
|
|
@staticmethod
|
|
def wheel_sha(index: dict[str, object]) -> str:
|
|
wheel = index["wheel"]
|
|
assert isinstance(wheel, dict)
|
|
digest = wheel["sha256"]
|
|
assert isinstance(digest, str)
|
|
return digest
|
|
|
|
def test_publishes_wheel_then_index(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
index, wheel = self.bundle(Path(tmp))
|
|
with patch(
|
|
"bot_bottle.release_publish.remote_bytes", return_value=None,
|
|
), patch(
|
|
"bot_bottle.release_publish.remote_sha256", return_value=None,
|
|
), patch("bot_bottle.release_publish.upload") as upload:
|
|
result = publish_bundle(index, wheel)
|
|
self.assertTrue(result.endswith("/bundle-index.json"))
|
|
self.assertEqual(2, upload.call_count)
|
|
self.assertIs(wheel, upload.call_args_list[0].args[1])
|
|
self.assertEqual(canonical_bytes(index), upload.call_args_list[1].args[1])
|
|
|
|
def test_exact_existing_bundle_is_idempotent(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
index, wheel = self.bundle(Path(tmp))
|
|
with patch(
|
|
"bot_bottle.release_publish.remote_bytes",
|
|
return_value=canonical_bytes(index),
|
|
), patch(
|
|
"bot_bottle.release_publish.remote_sha256",
|
|
return_value=self.wheel_sha(index),
|
|
), patch("bot_bottle.release_publish.upload") as upload:
|
|
publish_bundle(index, wheel)
|
|
upload.assert_not_called()
|
|
|
|
def test_rejects_partial_publication(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
index, wheel = self.bundle(Path(tmp))
|
|
with patch(
|
|
"bot_bottle.release_publish.remote_bytes", return_value=None,
|
|
), patch(
|
|
"bot_bottle.release_publish.remote_sha256",
|
|
return_value=self.wheel_sha(index),
|
|
):
|
|
with self.assertRaisesRegex(
|
|
ReleaseManifestError, "administrative cleanup",
|
|
):
|
|
publish_bundle(index, wheel)
|
|
|
|
def test_rejects_existing_bundle_with_different_index(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
index, wheel = self.bundle(Path(tmp))
|
|
with patch(
|
|
"bot_bottle.release_publish.remote_bytes", return_value=b"other",
|
|
), patch(
|
|
"bot_bottle.release_publish.remote_sha256",
|
|
return_value=self.wheel_sha(index),
|
|
):
|
|
with self.assertRaisesRegex(
|
|
ReleaseManifestError, "already differs",
|
|
):
|
|
publish_bundle(index, wheel)
|