diff --git a/semantic_release/hvcs/_base.py b/semantic_release/hvcs/_base.py index 41ad262da..b925d79ef 100644 --- a/semantic_release/hvcs/_base.py +++ b/semantic_release/hvcs/_base.py @@ -89,7 +89,7 @@ def upload_dists(self, tag: str, dist_glob: str) -> int: def create_release( self, tag: str, release_notes: str, prerelease: bool = False - ) -> int: + ) -> int | str: """ Create a release in a remote VCS, if supported """ @@ -104,7 +104,7 @@ def get_release_id_by_tag(self, tag: str) -> int | None: _not_supported(self, "get_release_id_by_tag") return None - def edit_release_notes(self, release_id: int, changelog: str) -> int: + def edit_release_notes(self, release_id: int, release_notes: str) -> int: """ Edit the changelog associated with a release, if supported """ @@ -113,7 +113,7 @@ def edit_release_notes(self, release_id: int, changelog: str) -> int: def create_or_update_release( self, tag: str, release_notes: str, prerelease: bool = False - ) -> int: + ) -> int | str: """ Create or update a release for the given tag in a remote VCS, attaching the given changelog, if supported diff --git a/semantic_release/hvcs/github.py b/semantic_release/hvcs/github.py index a140ef716..62189ab92 100644 --- a/semantic_release/hvcs/github.py +++ b/semantic_release/hvcs/github.py @@ -102,7 +102,7 @@ def create_release( :param tag: Tag to create release for :param release_notes: The release notes for this version :param prerelease: Whether or not this release should be created as a prerelease - :return: Whether the request succeeded + :return: the ID of the release """ log.info("Creating release for tag %s", tag) resp = self.session.post( diff --git a/semantic_release/hvcs/gitlab.py b/semantic_release/hvcs/gitlab.py index 376edbf5c..5ea04d236 100644 --- a/semantic_release/hvcs/gitlab.py +++ b/semantic_release/hvcs/gitlab.py @@ -87,34 +87,57 @@ def _get_repository_owner_and_name(self) -> tuple[str, str]: @logged_function(log) def create_release( self, tag: str, release_notes: str, prerelease: bool = False - ) -> bool: + ) -> str: """Post release changelog :param tag: Tag to create release for :param release_notes: The release notes for this version :param prerelease: This parameter has no effect - :return: The status of the request + :return: The tag of the release """ client = gitlab.Gitlab(self.api_url, private_token=self.token) client.auth() + log.info("Creating release for %s", tag) + # ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release + client.projects.get(self.owner + "/" + self.repo_name).releases.create( + { + "name": "Release " + tag, + "tag_name": tag, + "description": release_notes, + } + ) + log.info("Successfully created release for %s", tag) + return tag + + @logged_function(log) + def edit_release_notes(self, release_id: str, release_notes: str) -> str: # type: ignore # TODO make str types accepted here + client = gitlab.Gitlab(self.api_url, private_token=self.token) + client.auth() + log.info("Updating release %s", release_id) + + client.projects.get(self.owner + "/" + self.repo_name).releases.update( + release_id, + { + "description": release_notes, + }, + ) + return release_id + + @logged_function(log) + def create_or_update_release( + self, tag: str, release_notes: str, prerelease: bool = False + ) -> str: try: - log.info("Creating release for %s", tag) - # ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release - client.projects.get(self.owner + "/" + self.repo_name).releases.create( - { - "name": "Release " + tag, - "tag_name": tag, - "description": release_notes, - } + return self.create_release( + tag=tag, release_notes=release_notes, prerelease=prerelease ) - return True except gitlab.GitlabCreateError: - log.warning( + log.info( "Release %s could not be created for project %s/%s", tag, self.owner, self.repo_name, ) - return False + return self.edit_release_notes(release_id=tag, release_notes=release_notes) def compare_url(self, from_rev: str, to_rev: str) -> str: return f"https://{self.hvcs_domain}/{self.owner}/{self.repo_name}/-/compare/{from_rev}...{to_rev}" diff --git a/tests/const.py b/tests/const.py index 7f81c68c0..24ffaffb8 100644 --- a/tests/const.py +++ b/tests/const.py @@ -450,3 +450,5 @@ def _read_long_description(): * Extra cookies to enhance your experience * ~Removed~ simplified cookie opt-out handling logic """ + +RELEASE_NOTES = "# Release Notes" diff --git a/tests/unit/semantic_release/hvcs/test_gitea.py b/tests/unit/semantic_release/hvcs/test_gitea.py index 415bad5ea..c403001cc 100644 --- a/tests/unit/semantic_release/hvcs/test_gitea.py +++ b/tests/unit/semantic_release/hvcs/test_gitea.py @@ -12,7 +12,7 @@ from semantic_release.hvcs.gitea import Gitea from semantic_release.hvcs.token_auth import TokenAuth -from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES from tests.util import netrc_file @@ -199,7 +199,6 @@ def test_create_release_succeeds( default_gitea_client, status_code, prerelease, mock_release_id ): tag = "v1.0.0" - release_notes = "#TODO: Release Notes" with requests_mock.Mocker(session=default_gitea_client.session) as m: m.register_uri( "POST", @@ -208,7 +207,7 @@ def test_create_release_succeeds( status_code=status_code, ) assert ( - default_gitea_client.create_release(tag, release_notes, prerelease) + default_gitea_client.create_release(tag, RELEASE_NOTES, prerelease) == mock_release_id ) assert m.called @@ -225,7 +224,7 @@ def test_create_release_succeeds( assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": prerelease, } @@ -238,7 +237,6 @@ def test_create_release_fails( default_gitea_client, status_code, prerelease, mock_release_id ): tag = "v1.0.0" - release_notes = "#TODO: Release Notes" with requests_mock.Mocker(session=default_gitea_client.session) as m: m.register_uri( "POST", @@ -248,7 +246,7 @@ def test_create_release_fails( ) with pytest.raises(HTTPError): - default_gitea_client.create_release(tag, release_notes, prerelease) + default_gitea_client.create_release(tag, RELEASE_NOTES, prerelease) assert m.called assert len(m.request_history) == 1 @@ -264,7 +262,7 @@ def test_create_release_fails( assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": prerelease, } @@ -275,7 +273,6 @@ def test_should_create_release_using_token_or_netrc(default_gitea_client, token) default_gitea_client.token = token default_gitea_client.session.auth = None if not token else TokenAuth(token) tag = "v1.0.0" - release_notes = "#TODO: Release Notes" # Note write netrc file with DEFAULT_DOMAIN not DEFAULT_API_DOMAIN as can't # handle /api/v1 in file @@ -283,7 +280,7 @@ def test_should_create_release_using_token_or_netrc(default_gitea_client, token) machine=default_gitea_client.DEFAULT_DOMAIN ) as netrc, mock.patch.dict(os.environ, {"NETRC": netrc.name}, clear=True): m.register_uri("POST", gitea_api_matcher, json={"id": 1}, status_code=201) - assert default_gitea_client.create_release(tag, release_notes) == 1 + assert default_gitea_client.create_release(tag, RELEASE_NOTES) == 1 assert m.called assert len(m.request_history) == 1 assert m.last_request.method == "POST" @@ -311,7 +308,7 @@ def test_should_create_release_using_token_or_netrc(default_gitea_client, token) assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": False, } @@ -323,7 +320,7 @@ def test_request_has_no_auth_header_if_no_token_or_netrc(): with requests_mock.Mocker(session=client.session) as m: m.register_uri("POST", gitea_api_matcher, json={"id": 1}, status_code=201) - assert client.create_release("v1.0.0", "#TODO: Release Notes") == 1 + assert client.create_release("v1.0.0", RELEASE_NOTES) == 1 assert m.called assert len(m.request_history) == 1 assert m.last_request.method == "POST" @@ -373,7 +370,6 @@ def test_get_release_id_by_tag( def test_edit_release_notes_succeeds( default_gitea_client, status_code, mock_release_id ): - release_notes = "#TODO: Release Notes" with requests_mock.Mocker(session=default_gitea_client.session) as m: m.register_uri( "PATCH", @@ -382,7 +378,7 @@ def test_edit_release_notes_succeeds( status_code=status_code, ) assert ( - default_gitea_client.edit_release_notes(mock_release_id, release_notes) + default_gitea_client.edit_release_notes(mock_release_id, RELEASE_NOTES) == mock_release_id ) assert m.called @@ -397,13 +393,12 @@ def test_edit_release_notes_succeeds( release_id=mock_release_id, ) ) - assert m.last_request.json() == {"body": release_notes} + assert m.last_request.json() == {"body": RELEASE_NOTES} @pytest.mark.parametrize("status_code", (400, 404, 429, 500, 503)) @pytest.mark.parametrize("mock_release_id", range(3)) def test_edit_release_notes_fails(default_gitea_client, status_code, mock_release_id): - release_notes = "#TODO: Release Notes" with requests_mock.Mocker(session=default_gitea_client.session) as m: m.register_uri( "PATCH", @@ -413,7 +408,7 @@ def test_edit_release_notes_fails(default_gitea_client, status_code, mock_releas ) with pytest.raises(HTTPError): - default_gitea_client.edit_release_notes(mock_release_id, release_notes) + default_gitea_client.edit_release_notes(mock_release_id, RELEASE_NOTES) assert m.called assert len(m.request_history) == 1 @@ -427,7 +422,7 @@ def test_edit_release_notes_fails(default_gitea_client, status_code, mock_releas release_id=mock_release_id, ) ) - assert m.last_request.json() == {"body": release_notes} + assert m.last_request.json() == {"body": RELEASE_NOTES} # Note - mocking as the logic for the create/update of a release @@ -442,7 +437,6 @@ def test_create_or_update_release_when_create_succeeds( prerelease, ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" with mock.patch.object( default_gitea_client, "create_release" ) as mock_create_release, mock.patch.object( @@ -456,11 +450,11 @@ def test_create_or_update_release_when_create_succeeds( # client = Github(remote_url="git@github.com:something/somewhere.git") assert ( default_gitea_client.create_or_update_release( - tag, release_notes, prerelease + tag, RELEASE_NOTES, prerelease ) == mock_release_id ) - mock_create_release.assert_called_once_with(tag, release_notes, prerelease) + mock_create_release.assert_called_once_with(tag, RELEASE_NOTES, prerelease) mock_get_release_id_by_tag.assert_not_called() mock_edit_release_notes.assert_not_called() @@ -473,7 +467,6 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds( prerelease, ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" not_found = HTTPError("404 Not Found", response=Response()) not_found.response.status_code = 404 with mock.patch.object( @@ -489,12 +482,12 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds( # client = Github(remote_url="git@github.com:something/somewhere.git") assert ( default_gitea_client.create_or_update_release( - tag, release_notes, prerelease + tag, RELEASE_NOTES, prerelease ) == mock_release_id ) mock_get_release_id_by_tag.assert_called_once_with(tag) - mock_edit_release_notes.assert_called_once_with(mock_release_id, release_notes) + mock_edit_release_notes.assert_called_once_with(mock_release_id, RELEASE_NOTES) @pytest.mark.parametrize("prerelease", (True, False)) @@ -502,7 +495,6 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag( default_gitea_client, prerelease ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" not_found = HTTPError("404 Not Found", response=Response()) not_found.response.status_code = 404 with mock.patch.object( @@ -518,7 +510,7 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag( with pytest.raises(ValueError): default_gitea_client.create_or_update_release( - tag, release_notes, prerelease + tag, RELEASE_NOTES, prerelease ) mock_get_release_id_by_tag.assert_called_once_with(tag) diff --git a/tests/unit/semantic_release/hvcs/test_github.py b/tests/unit/semantic_release/hvcs/test_github.py index 4712fa94c..ec7683ab4 100644 --- a/tests/unit/semantic_release/hvcs/test_github.py +++ b/tests/unit/semantic_release/hvcs/test_github.py @@ -13,7 +13,7 @@ from semantic_release.hvcs.github import Github from semantic_release.hvcs.token_auth import TokenAuth -from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES from tests.util import netrc_file @@ -240,7 +240,6 @@ def test_create_release_succeeds( default_gh_client, status_code, prerelease, mock_release_id ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" with requests_mock.Mocker(session=default_gh_client.session) as m: m.register_uri( "POST", @@ -249,7 +248,7 @@ def test_create_release_succeeds( status_code=status_code, ) assert ( - default_gh_client.create_release(tag, release_notes, prerelease) + default_gh_client.create_release(tag, RELEASE_NOTES, prerelease) == mock_release_id ) assert m.called @@ -266,7 +265,7 @@ def test_create_release_succeeds( assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": prerelease, } @@ -276,14 +275,13 @@ def test_create_release_succeeds( @pytest.mark.parametrize("prerelease", (True, False)) def test_create_release_fails(default_gh_client, prerelease, status_code): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" with requests_mock.Mocker(session=default_gh_client.session) as m: m.register_uri( "POST", github_api_matcher, json={"id": 1}, status_code=status_code ) with pytest.raises(HTTPError): - default_gh_client.create_release(tag, release_notes, prerelease) + default_gh_client.create_release(tag, RELEASE_NOTES, prerelease) assert m.called assert len(m.request_history) == 1 @@ -299,7 +297,7 @@ def test_create_release_fails(default_gh_client, prerelease, status_code): assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": prerelease, } @@ -310,12 +308,11 @@ def test_should_create_release_using_token_or_netrc(default_gh_client, token): default_gh_client.token = token default_gh_client.session.auth = None if not token else TokenAuth(token) tag = "v1.0.0" - release_notes = "# TODO: Release Notes" with requests_mock.Mocker(session=default_gh_client.session) as m, netrc_file( machine=default_gh_client.DEFAULT_API_DOMAIN ) as netrc, mock.patch.dict(os.environ, {"NETRC": netrc.name}, clear=True): m.register_uri("POST", github_api_matcher, json={"id": 1}, status_code=201) - assert default_gh_client.create_release(tag, release_notes) == 1 + assert default_gh_client.create_release(tag, RELEASE_NOTES) == 1 assert m.called assert len(m.request_history) == 1 assert m.last_request.method == "POST" @@ -343,7 +340,7 @@ def test_should_create_release_using_token_or_netrc(default_gh_client, token): assert m.last_request.json() == { "tag_name": tag, "name": tag, - "body": release_notes, + "body": RELEASE_NOTES, "draft": False, "prerelease": False, } @@ -355,7 +352,7 @@ def test_request_has_no_auth_header_if_no_token_or_netrc(): with requests_mock.Mocker(session=client.session) as m: m.register_uri("POST", github_api_matcher, json={"id": 1}, status_code=201) - assert client.create_release("v1.0.0", "# TODO: Release Notes") == 1 + assert client.create_release("v1.0.0", RELEASE_NOTES) == 1 assert m.called assert len(m.request_history) == 1 assert m.last_request.method == "POST" @@ -373,7 +370,6 @@ def test_request_has_no_auth_header_if_no_token_or_netrc(): @pytest.mark.parametrize("status_code", [201]) @pytest.mark.parametrize("mock_release_id", range(3)) def test_edit_release_notes_succeeds(default_gh_client, status_code, mock_release_id): - release_notes = "# TODO: Release Notes" with requests_mock.Mocker(session=default_gh_client.session) as m: m.register_uri( "POST", @@ -382,7 +378,7 @@ def test_edit_release_notes_succeeds(default_gh_client, status_code, mock_releas status_code=status_code, ) assert ( - default_gh_client.edit_release_notes(mock_release_id, release_notes) + default_gh_client.edit_release_notes(mock_release_id, RELEASE_NOTES) == mock_release_id ) assert m.called @@ -397,20 +393,19 @@ def test_edit_release_notes_succeeds(default_gh_client, status_code, mock_releas release_id=mock_release_id, ) ) - assert m.last_request.json() == {"body": release_notes} + assert m.last_request.json() == {"body": RELEASE_NOTES} @pytest.mark.parametrize("status_code", (400, 404, 429, 500, 503)) def test_edit_release_notes_fails(default_gh_client, status_code): release_id = 420 - release_notes = "# TODO: Release Notes" with requests_mock.Mocker(session=default_gh_client.session) as m: m.register_uri( "POST", github_api_matcher, json={"id": release_id}, status_code=status_code ) with pytest.raises(HTTPError): - default_gh_client.edit_release_notes(release_id, release_notes) + default_gh_client.edit_release_notes(release_id, RELEASE_NOTES) assert m.called assert len(m.request_history) == 1 @@ -424,7 +419,7 @@ def test_edit_release_notes_fails(default_gh_client, status_code): release_id=release_id, ) ) - assert m.last_request.json() == {"body": release_notes} + assert m.last_request.json() == {"body": RELEASE_NOTES} @pytest.mark.parametrize( @@ -470,7 +465,6 @@ def test_create_or_update_release_when_create_succeeds( prerelease, ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" with mock.patch.object( default_gh_client, "create_release" ) as mock_create_release, mock.patch.object( @@ -483,10 +477,10 @@ def test_create_or_update_release_when_create_succeeds( mock_edit_release_notes.return_value = mock_release_id # client = Github(remote_url="git@github.com:something/somewhere.git") assert ( - default_gh_client.create_or_update_release(tag, release_notes, prerelease) + default_gh_client.create_or_update_release(tag, RELEASE_NOTES, prerelease) == mock_release_id ) - mock_create_release.assert_called_once_with(tag, release_notes, prerelease) + mock_create_release.assert_called_once_with(tag, RELEASE_NOTES, prerelease) mock_get_release_id_by_tag.assert_not_called() mock_edit_release_notes.assert_not_called() @@ -499,7 +493,6 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds( prerelease, ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" not_found = HTTPError("404 Not Found", response=Response()) not_found.response.status_code = 404 with mock.patch.object( @@ -514,11 +507,11 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds( mock_edit_release_notes.return_value = mock_release_id # client = Github(remote_url="git@github.com:something/somewhere.git") assert ( - default_gh_client.create_or_update_release(tag, release_notes, prerelease) + default_gh_client.create_or_update_release(tag, RELEASE_NOTES, prerelease) == mock_release_id ) mock_get_release_id_by_tag.assert_called_once_with(tag) - mock_edit_release_notes.assert_called_once_with(mock_release_id, release_notes) + mock_edit_release_notes.assert_called_once_with(mock_release_id, RELEASE_NOTES) @pytest.mark.parametrize("prerelease", (True, False)) @@ -526,7 +519,6 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag( default_gh_client, prerelease ): tag = "v1.0.0" - release_notes = "# TODO: Release Notes" not_found = HTTPError("404 Not Found", response=Response()) not_found.response.status_code = 404 with mock.patch.object( @@ -541,7 +533,7 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag( mock_edit_release_notes.return_value = None with pytest.raises(ValueError): - default_gh_client.create_or_update_release(tag, release_notes, prerelease) + default_gh_client.create_or_update_release(tag, RELEASE_NOTES, prerelease) mock_get_release_id_by_tag.assert_called_once_with(tag) mock_edit_release_notes.assert_not_called() diff --git a/tests/unit/semantic_release/hvcs/test_gitlab.py b/tests/unit/semantic_release/hvcs/test_gitlab.py index 274d10a87..a59e1b709 100644 --- a/tests/unit/semantic_release/hvcs/test_gitlab.py +++ b/tests/unit/semantic_release/hvcs/test_gitlab.py @@ -8,7 +8,7 @@ from semantic_release.hvcs.gitlab import Gitlab -from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER +from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES gitlab.Gitlab("") # instantiation necessary to discover gitlab ProjectManager @@ -17,6 +17,8 @@ A_GOOD_TAG = "v1.2.3" A_BAD_TAG = "v2.1.1-rc.1" A_LOCKED_TAG = "v0.9.0" +A_MISSING_TAG = "v1.0.0+missing" +AN_EXISTING_TAG = "v2.3.4+existing" # But note this is the only ref we're making a "fake" commit for, so # tests which need to query the remote for "a" ref, the exact sha for # which doesn't matter, all use this constant @@ -92,12 +94,12 @@ def __init__(self): pass def get(self, tag): - if tag == A_GOOD_TAG: + if tag in (A_GOOD_TAG, AN_EXISTING_TAG): return self._Tag() elif tag == A_LOCKED_TAG: return self._Tag(locked=True) else: - raise gitlab.exceptions.GitlabGetError + raise gitlab.exceptions.GitlabGetError() class _Tag: def __init__(self, locked=False): @@ -105,7 +107,7 @@ def __init__(self, locked=False): def set_release_description(self, release_notes): if self.locked: - raise gitlab.exceptions.GitlabUpdateError + raise gitlab.exceptions.GitlabUpdateError() class _Releases: def __init__(self): @@ -115,7 +117,12 @@ def create(self, input_): if input_["name"] and input_["tag_name"]: if input_["tag_name"] in (A_GOOD_TAG, A_LOCKED_TAG): return self._Release() - raise gitlab.exceptions.GitlabCreateError + raise gitlab.exceptions.GitlabCreateError() + + def update(self, tag, new_data): + if tag == A_MISSING_TAG: + raise gitlab.exceptions.GitlabUpdateError() + return self._Release() class _Release: def __init__(self, locked=False): @@ -314,15 +321,89 @@ def test_pull_request_url(default_gl_client, pr_number): ) -@pytest.mark.parametrize( - "tag, expected", - [ - (A_GOOD_TAG, True), - (A_LOCKED_TAG, True), - (A_BAD_TAG, False), - ], -) -def test_create_release(default_gl_client, tag, expected): - release_notes = "# TODO: Release Notes" +@pytest.mark.parametrize("tag", (A_GOOD_TAG, A_LOCKED_TAG)) +def test_create_release_succeeds(default_gl_client, tag): + with mock_gitlab(): + assert default_gl_client.create_release(tag, RELEASE_NOTES) == tag + + +def test_create_release_fails_with_bad_tag(default_gl_client): + with mock_gitlab(), pytest.raises(gitlab.GitlabCreateError): + default_gl_client.create_release(A_BAD_TAG, RELEASE_NOTES) + + +@pytest.mark.parametrize("tag", (A_GOOD_TAG, A_LOCKED_TAG)) +def test_update_release_succeeds(default_gl_client, tag): with mock_gitlab(): - assert default_gl_client.create_release(tag, release_notes) == expected + assert default_gl_client.edit_release_notes(tag, RELEASE_NOTES) == tag + + +def test_update_release_fails_with_missing_tag(default_gl_client): + with mock_gitlab(), pytest.raises(gitlab.GitlabUpdateError): + default_gl_client.edit_release_notes(A_MISSING_TAG, RELEASE_NOTES) + + +@pytest.mark.parametrize("prerelease", (True, False)) +def test_create_or_update_release_when_create_succeeds(default_gl_client, prerelease): + with mock.patch.object( + default_gl_client, "create_release" + ) as mock_create_release, mock.patch.object( + default_gl_client, "edit_release_notes" + ) as mock_edit_release_notes: + mock_create_release.return_value = A_GOOD_TAG + mock_edit_release_notes.return_value = A_GOOD_TAG + # client = Github(remote_url="git@github.com:something/somewhere.git") + assert ( + default_gl_client.create_or_update_release( + A_GOOD_TAG, RELEASE_NOTES, prerelease + ) + == A_GOOD_TAG + ) + mock_create_release.assert_called_once_with( + tag=A_GOOD_TAG, release_notes=RELEASE_NOTES, prerelease=prerelease + ) + mock_edit_release_notes.assert_not_called() + + +@pytest.mark.parametrize("prerelease", (True, False)) +def test_create_or_update_release_when_create_fails_and_update_succeeds( + default_gl_client, prerelease +): + bad_request = gitlab.GitlabCreateError("400 Bad Request") + with mock.patch.object( + default_gl_client, "create_release" + ) as mock_create_release, mock.patch.object( + default_gl_client, "edit_release_notes" + ) as mock_edit_release_notes: + mock_create_release.side_effect = bad_request + mock_edit_release_notes.return_value = A_GOOD_TAG + # client = Github(remote_url="git@github.com:something/somewhere.git") + assert ( + default_gl_client.create_or_update_release( + A_GOOD_TAG, RELEASE_NOTES, prerelease + ) + == A_GOOD_TAG + ) + mock_edit_release_notes.assert_called_once_with( + release_id=A_GOOD_TAG, release_notes=RELEASE_NOTES + ) + + +@pytest.mark.parametrize("prerelease", (True, False)) +def test_create_or_update_release_when_create_fails_and_update_fails( + default_gl_client, prerelease +): + bad_request = gitlab.GitlabCreateError("400 Bad Request") + not_found = gitlab.GitlabUpdateError("404 Not Found") + with mock.patch.object( + default_gl_client, "create_release" + ) as mock_create_release, mock.patch.object( + default_gl_client, "edit_release_notes" + ) as mock_edit_release_notes: + mock_create_release.side_effect = bad_request + mock_edit_release_notes.side_effect = not_found + + with pytest.raises(gitlab.GitlabUpdateError): + default_gl_client.create_or_update_release( + A_GOOD_TAG, RELEASE_NOTES, prerelease + )