release-gitter/release_gitter_test.py

201 lines
6.5 KiB
Python

from __future__ import annotations
import unittest
from tarfile import TarFile
from typing import Any
from typing import Callable
from typing import NamedTuple
from unittest.mock import MagicMock
from unittest.mock import mock_open
from unittest.mock import patch
from zipfile import ZipFile
import requests
import release_gitter
class TestExpression(NamedTuple):
t: unittest.TestCase
args: list[Any]
kwargs: dict[str, Any]
expected: Any
exception: type[Exception] | None = None
def run(self, f: Callable):
with self.t.subTest(f=f, args=self.args, kwargs=self.kwargs):
try:
result = f(*self.args, **self.kwargs)
self.t.assertIsNone(
self.exception,
f"Expected an exception of type {self.exception}, but found none",
)
self.t.assertEqual(self.expected, result)
return result
except Exception as e:
if self.exception and isinstance(e, self.exception):
return e
raise
class TestGeneral(unittest.TestCase):
def test_removesuffix(self):
for test_case in (
TestExpression(self, ["repo.git", ".git"], {}, "repo"),
TestExpression(self, ["repo", ".git"], {}, "repo"),
):
test_case.run(release_gitter.removesuffix)
class TestRemoteInfo(unittest.TestCase):
def test_parse_remote_info(self):
for test_case in (
TestExpression(
self,
["https://github.com/owner/repo"],
{},
release_gitter.GitRemoteInfo("github.com", "owner", "repo"),
None,
),
TestExpression(
self,
["git@github.com:owner/repo"],
{},
release_gitter.GitRemoteInfo("github.com", "owner", "repo"),
None,
),
TestExpression(
self,
["ssh://git@git.iamthefij.com/owner/repo"],
{},
release_gitter.GitRemoteInfo("git.iamthefij.com", "owner", "repo"),
None,
),
TestExpression(
self,
["https://git@example.com/repo"],
{},
None,
release_gitter.InvalidRemoteError,
),
):
test_case.run(release_gitter.parse_git_remote)
def test_generate_release_url(self):
for subtest in (
TestExpression(
self,
[release_gitter.GitRemoteInfo("github.com", "owner", "repo")],
{},
"https://api.github.com/repos/owner/repo/releases",
None,
),
TestExpression(
self,
[release_gitter.GitRemoteInfo("git.iamthefij.com", "owner", "repo")],
{},
"https://git.iamthefij.com/api/v1/repos/owner/repo/releases",
None,
),
TestExpression(
self,
[release_gitter.GitRemoteInfo("gitlab.com", "owner", "repo")],
{},
None,
release_gitter.InvalidRemoteError,
),
):
mock_response = MagicMock(spec=requests.Response)
mock_response.json = MagicMock()
if subtest.args[0].hostname == "git.iamthefij.com":
mock_response.json.return_value = {
"paths": {"/repos/{owner}/{repo}/releases": {}},
"basePath": "/api/v1",
}
with patch("requests.get", return_value=mock_response):
subtest.run(release_gitter.GitRemoteInfo.get_releases_url)
class TestVersionInfo(unittest.TestCase):
def test_no_cargo_file(self):
with patch("pathlib.Path.exists", return_value=False):
version = release_gitter.read_version()
self.assertIsNone(version)
@patch("pathlib.Path.exists", return_value=True)
@patch(
"pathlib.Path.open",
mock_open(read_data="\n".join(["[package]", 'version = "1.0.0"'])),
)
def test_cargo_file_has_version(self, *_):
version = release_gitter.read_version()
self.assertEqual(version, "1.0.0")
@patch("pathlib.Path.exists", return_value=True)
@patch(
"pathlib.Path.open",
mock_open(read_data="\n".join(["[package]"])),
)
def test_cargo_file_missing_version(self, *_):
with self.assertRaises(ValueError):
release_gitter.read_version()
@patch("release_gitter.ZipFile", autospec=True)
@patch("release_gitter.BytesIO", autospec=True)
class TestContentTypeDetection(unittest.TestCase):
def test_asset_encoding_priority(self, *_):
package = release_gitter.get_asset_package(
{
"content_type": "application/x-tar",
"name": "test.zip",
},
MagicMock(spec=["raw", "content"]),
)
# Tar should take priority over the file name zip extension
self.assertIsInstance(package._package, TarFile)
def test_fallback_to_supported_encoding(self, *_):
package = release_gitter.get_asset_package(
{
"content_type": "application/octetstream",
"name": "test.zip",
},
MagicMock(spec=["raw", "content"]),
)
# Should fall back to zip extension
self.assertIsInstance(package._package, ZipFile)
def test_missing_only_name_content_type(self, *_):
package = release_gitter.get_asset_package(
{
"name": "test.zip",
},
MagicMock(spec=["raw", "content"]),
)
# Should fall back to zip extension
self.assertIsInstance(package._package, ZipFile)
def test_no_content_types(self, *_):
with self.assertRaises(release_gitter.UnsupportedContentTypeError):
release_gitter.get_asset_package(
{
"name": "test",
},
MagicMock(spec=["raw", "content"]),
)
def test_no_supported_content_types(self, *_):
with self.assertRaises(release_gitter.UnsupportedContentTypeError):
release_gitter.get_asset_package(
{
"content_type": "application/octetstream",
"name": "test",
},
MagicMock(spec=["raw", "content"]),
)
if __name__ == "__main__":
unittest.main()