diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 0792c56..6905d5c 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -21,10 +21,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Set up JDK 19
+ - name: Set up JDK 21
uses: actions/setup-java@v4
with:
- java-version: '19'
+ java-version: '21'
distribution: 'temurin'
cache: maven
- name: Build with Maven
diff --git a/coverage/pom.xml b/coverage/pom.xml
index 4d02247..89ca2ea 100644
--- a/coverage/pom.xml
+++ b/coverage/pom.xml
@@ -92,6 +92,11 @@
semver-release-gitlab
${project.version}
+
+ io.github.sam42r
+ semver-release-codeberg
+ ${project.version}
+
io.github.sam42r
diff --git a/docs/README.md b/docs/README.md
index e81fb9b..3f214cc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -217,7 +217,7 @@ or by setting corresponding system property.
| | renderCommitLinks | | `true` | [true|false] | Generate commit links |
| | renderReleaseLinks | | `true` | [true|false] | Generate release links |
| | renderBody | | `false` | [true|false] | Include commit message body |
-| release | publisher-name | | `GitHub` | [Github|Gitlab] | Release publisher |
+| release | publisher-name | | `GitHub` | [Github|Gitlab|Codeberg] | Release publisher |
| | publish | | `false` | [true|false] | publish release to remote |
| | username | | | | |
| | password | | | | |
diff --git a/pom.xml b/pom.xml
index a1b46a0..a553a90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,7 @@
semver-release-api
semver-release-github
semver-release-gitlab
+ semver-release-codeberg
semver-maven-plugin
coverage
diff --git a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java
index c80b279..b9c8af4 100644
--- a/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java
+++ b/semver-maven-plugin/src/test/java/io/github/sam42r/semver/util/BundleHelper.java
@@ -27,7 +27,8 @@ public class BundleHelper {
"semver-scm-subversion",
"semver-release-api",
"semver-release-github",
- "semver-release-gitlab"
+ "semver-release-gitlab",
+ "semver-release-codeberg"
};
public static void main(String[] args) {
diff --git a/semver-release-api/pom.xml b/semver-release-api/pom.xml
index 7dc9233..9162ad2 100644
--- a/semver-release-api/pom.xml
+++ b/semver-release-api/pom.xml
@@ -14,8 +14,8 @@
${project.groupId}:${project.artifactId}
- 19
- 19
+ 21
+ 21
UTF-8
@@ -30,5 +30,10 @@
lombok
provided
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.2
+
diff --git a/semver-release-api/src/main/java/io/github/sam24r/semver/release/AbstractRestApiPublisher.java b/semver-release-api/src/main/java/io/github/sam24r/semver/release/AbstractRestApiPublisher.java
new file mode 100644
index 0000000..1333ae9
--- /dev/null
+++ b/semver-release-api/src/main/java/io/github/sam24r/semver/release/AbstractRestApiPublisher.java
@@ -0,0 +1,60 @@
+package io.github.sam24r.semver.release;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.github.sam42r.semver.model.release.ReleaseInfo;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+
+@RequiredArgsConstructor
+public abstract class AbstractRestApiPublisher implements ReleasePublisher {
+
+ @Getter
+ private final ObjectMapper objectMapper = new ObjectMapper();
+
+ private final String baseUrl;
+
+ @Override
+ public void publish(@NonNull String scheme, @NonNull String instance, @NonNull String group, @NonNull String project, @NonNull ReleaseInfo releaseInfo) throws ReleaseException {
+ var projectPath = "%s/%s".formatted(group, project);
+ var encodedProjectPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8);
+ var uri = URI.create(baseUrl.formatted(scheme, instance, encodedProjectPath));
+ var payload = generatePayload(releaseInfo);
+
+ try (var httpClient = HttpClient.newHttpClient()) {
+ var json = objectMapper.writeValueAsString(payload);
+
+ var httpRequest = HttpRequest.newBuilder()
+ .uri(uri)
+ .headers(headers())
+ .POST(HttpRequest.BodyPublishers.ofString(json))
+ .build();
+
+ var response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+
+ if (response.statusCode() != 201) {
+ throw new ReleaseException("Release API does return with HTTP-%d - %s".formatted(
+ response.statusCode(), getErrorMessage(response.body())));
+ }
+ } catch (IOException e) {
+ throw new ReleaseException(e);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new ReleaseException(e);
+ }
+ }
+
+ protected abstract Object generatePayload(ReleaseInfo releaseInfo);
+
+ protected abstract String[] headers();
+
+ protected abstract String getErrorMessage(String responseBody) throws IOException;
+}
diff --git a/semver-release-codeberg/pom.xml b/semver-release-codeberg/pom.xml
new file mode 100644
index 0000000..d8235e8
--- /dev/null
+++ b/semver-release-codeberg/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+
+ io.github.sam42r
+ semver-parent
+ 1.9.1
+
+
+ semver-release-codeberg
+ ${project.groupId}:${project.artifactId}
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ io.github.sam42r
+ semver-release-api
+ ${project.version}
+
+
+
+ javax.inject
+ javax.inject
+ provided
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.mockito
+ mockito-inline
+ 4.8.1
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mock-server
+ mockserver-junit-jupiter-no-dependencies
+ test
+
+
+
+
\ No newline at end of file
diff --git a/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisher.java b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisher.java
new file mode 100644
index 0000000..7da34f1
--- /dev/null
+++ b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisher.java
@@ -0,0 +1,52 @@
+package io.github.sam42r.semver.release;
+
+import io.github.sam24r.semver.release.AbstractRestApiPublisher;
+import io.github.sam42r.semver.model.release.ProviderSpec;
+import io.github.sam42r.semver.model.release.ReleaseInfo;
+import io.github.sam42r.semver.release.model.ApiError;
+import io.github.sam42r.semver.release.model.CreateReleaseOption;
+
+import java.io.IOException;
+
+public class CodebergPublisher extends AbstractRestApiPublisher {
+
+ private final String token;
+
+ public CodebergPublisher(String baseUrl, String token) {
+ super(baseUrl);
+ this.token = token;
+ }
+
+ @Override
+ protected Object generatePayload(ReleaseInfo releaseInfo) {
+ return CreateReleaseOption.builder()
+ .tagName(releaseInfo.tagName())
+ .name(releaseInfo.name())
+ .body(releaseInfo.description())
+ .build();
+ }
+
+ @Override
+ protected String[] headers() {
+ return new String[]{
+ "Content-Type", "application/json",
+ "Accept", "application/json",
+ "Authorization", token
+ };
+ }
+
+ @Override
+ protected String getErrorMessage(String responseBody) throws IOException {
+ var apiError = getObjectMapper().readValue(responseBody, ApiError.class);
+ return apiError.getMessage();
+ }
+
+ @Override
+ public ProviderSpec providerSpec() {
+ return new ProviderSpec(
+ "%s://%s/%s/%s/issues/%s",
+ "%s://%s/%s/%s/commit/%s",
+ "%s://%s/%s/%s/compare/%s...%s"
+ );
+ }
+}
diff --git a/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisherFactory.java b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisherFactory.java
new file mode 100644
index 0000000..23893ac
--- /dev/null
+++ b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/CodebergPublisherFactory.java
@@ -0,0 +1,20 @@
+package io.github.sam42r.semver.release;
+
+import io.github.sam24r.semver.release.ReleasePublisher;
+import lombok.NonNull;
+
+import javax.inject.Named;
+
+@Named("Codeberg")
+public class CodebergPublisherFactory implements io.github.sam24r.semver.release.ReleasePublisherFactory {
+
+ @Override
+ public @NonNull ReleasePublisher getInstance(String username, String password) {
+ return getInstance("%s://%s/api/v1/repos/%s/releases", username, password);
+ }
+
+ @Override
+ public @NonNull ReleasePublisher getInstance(@NonNull String baseUrl, String username, String password) {
+ return new CodebergPublisher(baseUrl, password);
+ }
+}
diff --git a/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/ApiError.java b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/ApiError.java
new file mode 100644
index 0000000..5b25bab
--- /dev/null
+++ b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/ApiError.java
@@ -0,0 +1,21 @@
+package io.github.sam42r.semver.release.model;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * Generic API error used for:
+ *
+ * - 404: APINotFound is a not found error response
+ * - 409: APIError is error format response
+ * - 422: APIValidationError is error format response related to input validation
+ *
+ */
+@Data
+public class ApiError {
+
+ private String message;
+ private String url;
+ private List errors;
+}
diff --git a/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/CreateReleaseOption.java b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/CreateReleaseOption.java
new file mode 100644
index 0000000..ff952d1
--- /dev/null
+++ b/semver-release-codeberg/src/main/java/io/github/sam42r/semver/release/model/CreateReleaseOption.java
@@ -0,0 +1,28 @@
+package io.github.sam42r.semver.release.model;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * CreateReleaseOption options when creating a release.
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CreateReleaseOption {
+
+ private String body;
+ private boolean draft;
+ @JsonProperty("hide_archive_links")
+ private boolean hideArchiveLinks;
+ private String name;
+ private boolean prerelease;
+ @JsonProperty("tag_name")
+ private String tagName;
+ @JsonProperty("target_commitish")
+ private String targetCommitish;
+}
diff --git a/semver-release-codeberg/src/test/java/io/github/sam42r/semver/release/CodebergPublisherTest.java b/semver-release-codeberg/src/test/java/io/github/sam42r/semver/release/CodebergPublisherTest.java
new file mode 100644
index 0000000..2fe2542
--- /dev/null
+++ b/semver-release-codeberg/src/test/java/io/github/sam42r/semver/release/CodebergPublisherTest.java
@@ -0,0 +1,149 @@
+package io.github.sam42r.semver.release;
+
+import io.github.sam24r.semver.release.ReleaseException;
+import io.github.sam24r.semver.release.ReleasePublisher;
+import io.github.sam42r.semver.model.release.ReleaseInfo;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+import org.mockserver.client.MockServerClient;
+import org.mockserver.junit.jupiter.MockServerExtension;
+import org.mockserver.matchers.Times;
+import org.mockserver.verify.VerificationTimes;
+
+import java.io.IOException;
+import java.net.http.HttpClient;
+import java.time.LocalDateTime;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.*;
+import static org.mockserver.model.HttpRequest.request;
+import static org.mockserver.model.HttpResponse.response;
+
+@ExtendWith(MockServerExtension.class)
+class CodebergPublisherTest {
+
+ private ReleasePublisher uut;
+
+ @BeforeEach
+ void setup(MockServerClient client) {
+ uut = new CodebergPublisherFactory().getInstance(null, "token");
+ client.reset();
+ }
+
+ @Test
+ void shouldCreateRelease(MockServerClient client) throws ReleaseException {
+ client.when(request(), Times.exactly(1))
+ .respond(response()
+ .withStatusCode(201)
+ .withBody("""
+ {
+ "tag_name": "v1.0.0",
+ "name": "v1.0.0",
+ "created_at": "2024-01-01T12:00:00.000Z",
+ "published_at": "2024-01-01T12:00:00.000Z"
+ }
+ """)
+ );
+
+ var release = new ReleaseInfo(
+ "v1.0.0",
+ "v1.0.0",
+ "# Release v1.0.0",
+ LocalDateTime.now()
+ );
+
+ uut.publish(
+ "http",
+ "localhost:%d".formatted(client.getPort()),
+ "JUnit",
+ "Test",
+ release);
+
+ client.verify(request().withMethod("POST")
+ .withPath("/api/v1/repos/JUnit%2FTest/releases")
+ .withHeader("Content-Type", "application/json")
+ .withHeader("Accept", "application/json")
+ .withHeader("Authorization", "token"),
+ VerificationTimes.exactly(1));
+ }
+
+ @Test
+ void shouldThrowOnHttpError(MockServerClient client) {
+ client.when(request(), Times.exactly(1))
+ .respond(response()
+ .withStatusCode(404)
+ .withBody("""
+ {
+ "message": "GetUserByName",
+ "url": "https://codeberg.org/api/swagger",
+ "errors": [
+ "user redirect does not exist [name: junit]"
+ ]
+ }
+ """)
+ );
+
+ var release = new ReleaseInfo(
+ "v1.0.0",
+ "v1.0.0",
+ "# Release v1.0.0",
+ LocalDateTime.now()
+ );
+
+ assertThatThrownBy(() -> uut.publish(
+ "http",
+ "localhost:%d".formatted(client.getPort()),
+ "JUnit",
+ "Test",
+ release))
+ .isInstanceOf(ReleaseException.class)
+ .hasMessage("Release API does return with HTTP-404 - GetUserByName");
+
+ client.verify(request().withMethod("POST")
+ .withPath("/api/v1/repos/JUnit%2FTest/releases")
+ .withHeader("Content-Type", "application/json")
+ .withHeader("Accept", "application/json")
+ .withHeader("Authorization", "token"),
+ VerificationTimes.exactly(1));
+ }
+
+ @ParameterizedTest
+ @MethodSource("thrownExceptions")
+ void shouldThrowOnException(Exception thrownException, String expectedMessage, MockServerClient client) throws IOException, InterruptedException {
+ try (var httpClientStatic = mockStatic(HttpClient.class)) {
+ var httpClient = mock(HttpClient.class);
+ when(httpClient.send(Mockito.any(), Mockito.any())).thenThrow(thrownException);
+
+ httpClientStatic.when(HttpClient::newHttpClient).thenReturn(httpClient);
+
+ var release = new ReleaseInfo(
+ "v1.0.0",
+ "v1.0.0",
+ "# Release v1.0.0",
+ LocalDateTime.now()
+ );
+
+ assertThatThrownBy(() -> uut.publish(
+ "http",
+ "localhost:%d".formatted(client.getPort()),
+ "JUnit",
+ "Test",
+ release))
+ .isInstanceOf(ReleaseException.class)
+ .hasMessage(expectedMessage);
+ }
+ }
+
+ private static Stream thrownExceptions() {
+ return Stream.of(
+ Arguments.of(new IOException("JUnit"), "java.io.IOException: JUnit"),
+ Arguments.of(new InterruptedException("JUnit"), "java.lang.InterruptedException: JUnit")
+ );
+ }
+}
diff --git a/semver-release-gitlab/pom.xml b/semver-release-gitlab/pom.xml
index 9a5cf5f..ce1c70a 100644
--- a/semver-release-gitlab/pom.xml
+++ b/semver-release-gitlab/pom.xml
@@ -14,8 +14,8 @@
${project.groupId}:${project.artifactId}
- 19
- 19
+ 21
+ 21
UTF-8
@@ -40,16 +40,17 @@
org.slf4j
slf4j-api
-
- com.fasterxml.jackson.core
- jackson-databind
- 2.17.2
-
org.junit.jupiter
junit-jupiter
test
+
+ org.mockito
+ mockito-inline
+ 4.8.1
+ test
+
org.assertj
assertj-core
diff --git a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java
index c621e1f..3ab5f6c 100644
--- a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java
+++ b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/GitlabPublisher.java
@@ -1,22 +1,10 @@
package io.github.sam42r.semver.release;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.github.sam24r.semver.release.ReleaseException;
+import io.github.sam24r.semver.release.AbstractRestApiPublisher;
import io.github.sam24r.semver.release.ReleasePublisher;
import io.github.sam42r.semver.model.release.ProviderSpec;
import io.github.sam42r.semver.model.release.ReleaseInfo;
import io.github.sam42r.semver.release.model.GitlabRelease;
-import lombok.NonNull;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.nio.charset.StandardCharsets;
/**
* {@link ReleasePublisher} for GitLab.
@@ -24,51 +12,17 @@
* @see
* Releases API | GitLab
*/
-@Slf4j
-@RequiredArgsConstructor
-public class GitlabPublisher implements ReleasePublisher {
-
- private final ObjectMapper objectMapper = new ObjectMapper();
+public class GitlabPublisher extends AbstractRestApiPublisher {
- private final String baseUrl;
private final String token;
- @Override
- public void publish(
- @NonNull String scheme,
- @NonNull String instance,
- @NonNull String group,
- @NonNull String project,
- @NonNull ReleaseInfo releaseInfo
- ) throws ReleaseException {
- var projectPath = "%s/%s".formatted(group, project);
- var encodedProjectPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8);
- var uri = URI.create(baseUrl.formatted(scheme, instance, encodedProjectPath));
- var payload = generatePayload(releaseInfo);
-
- try {
- var json = objectMapper.writeValueAsString(payload);
-
- var httpRequest = HttpRequest.newBuilder()
- .uri(uri)
- .header("Content-Type", "application/json")
- .header("PRIVATE-TOKEN", token)
- .POST(HttpRequest.BodyPublishers.ofString(json))
- .build();
-
- var response = HttpClient.newHttpClient()
- .send(httpRequest, HttpResponse.BodyHandlers.ofString());
-
- if (response.statusCode() != 201) {
- throw new ReleaseException("Release API does return with HTTP-%d - %s".formatted(
- response.statusCode(), response.body()));
- }
- } catch (IOException | InterruptedException e) {
- throw new ReleaseException(e);
- }
+ public GitlabPublisher(String baseUrl, String token) {
+ super(baseUrl);
+ this.token = token;
}
- private GitlabRelease generatePayload(ReleaseInfo releaseInfo) {
+ @Override
+ protected Object generatePayload(ReleaseInfo releaseInfo) {
return GitlabRelease.builder()
.tagName(releaseInfo.tagName())
.name(releaseInfo.name())
@@ -76,6 +30,19 @@ private GitlabRelease generatePayload(ReleaseInfo releaseInfo) {
.build();
}
+ @Override
+ protected String[] headers() {
+ return new String[]{
+ "Content-Type", "application/json",
+ "PRIVATE-TOKEN", token
+ };
+ }
+
+ @Override
+ protected String getErrorMessage(String responseBody) {
+ return responseBody;
+ }
+
@Override
public ProviderSpec providerSpec() {
return new ProviderSpec(
diff --git a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/model/GitlabRelease.java b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/model/GitlabRelease.java
index 29cca09..c89676d 100644
--- a/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/model/GitlabRelease.java
+++ b/semver-release-gitlab/src/main/java/io/github/sam42r/semver/release/model/GitlabRelease.java
@@ -5,7 +5,6 @@
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
-import lombok.NonNull;
import java.time.LocalDateTime;
import java.util.List;
@@ -18,7 +17,6 @@
*/
@Data
@Builder
-@NonNull
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GitlabRelease {
diff --git a/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java b/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java
index 0161f2b..da13c44 100644
--- a/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java
+++ b/semver-release-gitlab/src/test/java/io/github/sam42r/semver/release/GitlabPublisherTest.java
@@ -6,14 +6,22 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
import org.mockserver.client.MockServerClient;
import org.mockserver.junit.jupiter.MockServerExtension;
import org.mockserver.matchers.Times;
import org.mockserver.verify.VerificationTimes;
+import java.io.IOException;
+import java.net.http.HttpClient;
import java.time.LocalDateTime;
+import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.*;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
@@ -24,11 +32,7 @@ class GitlabPublisherTest {
@BeforeEach
void setup(MockServerClient client) {
- uut = new GitlabPublisherFactory().getInstance(
- "%s://%s/api/v4/projects/%s/releases",
- null,
- "token"
- );
+ uut = new GitlabPublisherFactory().getInstance(null, "token");
client.reset();
}
@@ -69,7 +73,7 @@ void shouldCreateRelease(MockServerClient client) throws ReleaseException {
}
@Test
- void shouldThrowOnHttp500(MockServerClient client) {
+ void shouldThrowOnHttpError(MockServerClient client) {
client.when(request(), Times.exactly(1))
.respond(response()
.withStatusCode(500)
@@ -98,4 +102,38 @@ void shouldThrowOnHttp500(MockServerClient client) {
.withHeader("PRIVATE-TOKEN", "token"),
VerificationTimes.exactly(1));
}
+
+ @ParameterizedTest
+ @MethodSource("thrownExceptions")
+ void shouldThrowOnException(Exception thrownException, String expectedMessage, MockServerClient client) throws IOException, InterruptedException {
+ try (var httpClientStatic = mockStatic(HttpClient.class)) {
+ var httpClient = mock(HttpClient.class);
+ when(httpClient.send(Mockito.any(), Mockito.any())).thenThrow(thrownException);
+
+ httpClientStatic.when(HttpClient::newHttpClient).thenReturn(httpClient);
+
+ var release = new ReleaseInfo(
+ "v1.0.0",
+ "v1.0.0",
+ "# Release v1.0.0",
+ LocalDateTime.now()
+ );
+
+ assertThatThrownBy(() -> uut.publish(
+ "http",
+ "localhost:%d".formatted(client.getPort()),
+ "JUnit",
+ "Test",
+ release))
+ .isInstanceOf(ReleaseException.class)
+ .hasMessage(expectedMessage);
+ }
+ }
+
+ private static Stream thrownExceptions() {
+ return Stream.of(
+ Arguments.of(new IOException("JUnit"), "java.io.IOException: JUnit"),
+ Arguments.of(new InterruptedException("JUnit"), "java.lang.InterruptedException: JUnit")
+ );
+ }
}