import re import tempfile import unittest from pathlib import Path from unittest.mock import patch from scripts import publish_release class Response: def __init__(self, body=b'{"id": 1}', status=201, headers=None): self.body = body self.status = status self.headers = headers or {} def __enter__(self): return self def __exit__(self, *args): return False def read(self, size=-1): return self.body if size < 0 else self.body[:size] class PublishReleaseTest(unittest.TestCase): def test_upload_is_multipart_with_attachment_field(self): with tempfile.TemporaryDirectory() as temporary: path = Path(temporary) / "package.crx" path.write_bytes(b"Cr24\x03payload") with patch("scripts.publish_release.urlopen", return_value=Response()) as request: publish_release._upload_asset( "https://gitea.example/api/v1", "secret-token", "/repos/o/r", 4, path, "package.crx" ) sent = request.call_args.args[0] body = sent.data content_type = sent.get_header("Content-type") boundary = re.search(r"boundary=([^;]+)", content_type).group(1).encode("ascii") self.assertIn(b'name="attachment"', body) self.assertIn(b"filename=\"package.crx\"", body) self.assertIn(b"Cr24\x03payload", body) self.assertTrue(body.endswith(b"--" + boundary + b"--\r\n")) def test_edit_and_delete_use_release_scoped_attachment_path(self): with patch("scripts.publish_release.api_call") as call: publish_release._edit_asset("base", "token", "/repos/o/r", 9, 12, "new.crx") publish_release._delete_asset("base", "token", "/repos/o/r", 9, 12) self.assertEqual(call.call_args_list[0].args[3], "/repos/o/r/releases/9/assets/12") self.assertEqual(call.call_args_list[1].args[3], "/repos/o/r/releases/9/assets/12") def test_public_crx_check_is_anonymous_and_allows_content_type_parameters(self): with tempfile.TemporaryDirectory() as temporary: updates = Path(temporary) / "updates.xml" updates.write_text( '', encoding="utf-8", ) with patch( "scripts.publish_release.urlopen", return_value=Response( b"Cr24\x03payload", status=200, headers={"Content-Type": "application/x-chrome-extension; charset=binary"}, ), ) as request: publish_release.verify_public_crx(updates) self.assertIsNone(request.call_args.args[0].get_header("Authorization")) def test_second_replacement_is_single_asset_and_version_downgrades_are_skipped(self): assets = [] next_id = iter(range(1, 10)) def upload(*args): asset = {"id": next(next_id), "name": args[-1]} assets.append(asset) return asset def edit(*args): attachment_id, name = args[-2:] next(asset for asset in assets if asset["id"] == attachment_id)["name"] = name def delete(*args): attachment_id = args[-1] assets[:] = [asset for asset in assets if asset["id"] != attachment_id] with tempfile.TemporaryDirectory() as temporary: path = Path(temporary) / "package.crx" path.write_bytes(b"data") with patch("scripts.publish_release.release_assets", side_effect=lambda *args: assets.copy()), \ patch("scripts.publish_release._upload_asset", side_effect=upload), \ patch("scripts.publish_release._edit_asset", side_effect=edit), \ patch("scripts.publish_release._delete_asset", side_effect=delete): publish_release.replace_asset("base", "token", "/repos/o/r", 9, path) publish_release.replace_asset("base", "token", "/repos/o/r", 9, path) self.assertEqual(assets, [{"id": 2, "name": "package.crx"}]) self.assertFalse(publish_release.should_publish_updates("2.4.0", "2.3.9")) self.assertFalse(publish_release.should_publish_updates("2.4.0", "2.4.0")) self.assertTrue(publish_release.should_publish_updates("2.4.0", "2.4.1")) if __name__ == "__main__": unittest.main()