diff --git a/Jenkinsfile b/Jenkinsfile index f6bf4edfd8d3fbf200945180b64bc908e1bf874d..a4c66b8630e7fc3fe05d4887c3a78b333740cbaa 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,7 +13,6 @@ pipeline { IMAGE_TAG = generateImageTag() HELM_CHART_VERSION = generateHelmChartVersion() BUILD_PROFILE = getBuildProfile() - BUILD_NUMBER = env.BUILD_NUMBER } options { @@ -49,7 +48,7 @@ pipeline { } configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { - sh "mvn -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3 -DelasticTests.disabled=true -Dbuild.number=$BUILD_NUMBER" + sh 'mvn -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3 -DelasticTests.disabled=true -Dbuild.number=$BUILD_NUMBER' } } } @@ -117,7 +116,7 @@ pipeline { configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) { - sh "mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER" + sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER' } } } diff --git a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java index cd66647d10b09a38b229b1931cf2e65bbef5733a..b8db6e7eb5b23571f40ae99be833bdddd935dcc0 100644 --- a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java +++ b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java @@ -34,7 +34,7 @@ import lombok.RequiredArgsConstructor; @Component @RequiredArgsConstructor @NonNullApi -class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> { +public class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> { static final String REL_CONFIGURATION = "configuration"; private final RepositoryRestProperties restProperties; @@ -46,7 +46,6 @@ class RootModelAssembler implements RepresentationModelAssembler<Root, EntityMod return EntityModel.of( root, Link.of(configLink.toUriString(), REL_CONFIGURATION), - rootLink.withSelfRel() - ); + rootLink.withSelfRel()); } } diff --git a/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java b/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java index 646d2c0b24579a6d7cfd502fbbdf58cfc530806a..740d33c05999c8ee00d6135e636bb20aa2d12493 100644 --- a/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java +++ b/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerITCase.java @@ -1,112 +1,87 @@ -/* - * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ package de.ozgcloud.admin.errorhandling; -import static de.ozgcloud.admin.errorhandling.AdminExceptionHandler.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.stream.Stream; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.HttpStatus; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import de.ozgcloud.admin.RootController; +import de.ozgcloud.admin.RootModelAssembler; +import de.ozgcloud.common.test.ITCase; import lombok.SneakyThrows; -class AdminExceptionHandlerITCase { +@ITCase +@AutoConfigureMockMvc +@WithMockUser +public class AdminExceptionHandlerITCase { + @Autowired private MockMvc mockMvc; - @BeforeEach - void setup() { - mockMvc = MockMvcBuilders - .standaloneSetup(new TestErrorController()) - .setControllerAdvice(new AdminExceptionHandler()).build(); - } + @MockBean + private RootModelAssembler modelAssembler; - @DisplayName("Error handler") @Nested - class TestErrorHandler { - + class TestExceptions { @ParameterizedTest @MethodSource("exceptionAndExpectedStatus") @SneakyThrows void shouldHandleExceptionWithStatus(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) { - var result = doPerformWithError(exceptionClass); + when(modelAssembler.toModel(any())).thenThrow(TestErrorController.EXCEPTION_PRODUCER.get(exceptionClass).produceException()); + + var result = performGet(); + + System.out.println(result.andReturn().getResponse().getContentAsString()); result.andExpect(status().is(expectedStatus.value())); } - @ParameterizedTest - @MethodSource("exceptionAndExpectedStatus") + @Test @SneakyThrows - void shouldRespondWithStatusInBody(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) { - var result = doPerformWithError(exceptionClass); + void shouldHandleRuntimeExceptionExceptionWithStatus() { + when(modelAssembler.toModel(any())).thenThrow(new RuntimeException("Message")); - result.andExpect(jsonPath("$.status").value(expectedStatus.value())); - } + var result = performGet(); - private static Stream<Arguments> exceptionAndExpectedStatus() { - return STATUS_BY_EXCEPTION.entrySet().stream().map(kv -> Arguments.of(kv.getKey(), kv.getValue())); - } + System.out.println(result.andReturn().getResponse().getContentAsString()); - @SneakyThrows - private ResultActions doPerformWithError(Class<? extends Exception> exceptionClass) { - return mockMvc.perform(get("/test-error").param("errorClassName", exceptionClass.getName())); + result.andExpect(status().isInternalServerError()); } - } - @DisplayName("ResourceNotFound error") - @Nested - class TestResourceNotFoundError { - - @Test + @ParameterizedTest + @MethodSource("exceptionAndExpectedStatus") @SneakyThrows - void shouldHaveStatus() { - var result = doRequestUnknown(); + void shouldRespondWithStatusInBody(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) { + when(modelAssembler.toModel(any())).thenThrow(exceptionClass); - result.andExpect(status().is(HttpStatus.NOT_FOUND.value())); - } + var result = performGet(); - @Test - @SneakyThrows - void shouldRespondWithStatusInBody() { - var result = doRequestUnknown(); + result.andExpect(jsonPath("$.status").value(expectedStatus.value())); + } - result.andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())); + private static Stream<Arguments> exceptionAndExpectedStatus() { + return AdminExceptionHandler.STATUS_BY_EXCEPTION.entrySet().stream().map(kv -> Arguments.of(kv.getKey(), kv.getValue())); } @SneakyThrows - private ResultActions doRequestUnknown() { - return mockMvc.perform(get("/api/unknown")); + private ResultActions performGet() { + return mockMvc.perform(get(RootController.PATH)); } } + } diff --git a/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerTest.java b/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8f62290e40d9c31147f0497fc40330392a21dd65 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/errorhandling/AdminExceptionHandlerTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.errorhandling; + +import static de.ozgcloud.admin.errorhandling.AdminExceptionHandler.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.http.HttpStatus; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import lombok.SneakyThrows; + +class AdminExceptionHandlerTest { + + private MockMvc mockMvc; + + @BeforeEach + void setup() { + mockMvc = MockMvcBuilders + .standaloneSetup(new TestErrorController()) + .setControllerAdvice(new AdminExceptionHandler()).build(); + } + + @DisplayName("Error handler") + @Nested + class TestErrorHandler { + + @ParameterizedTest + @MethodSource("exceptionAndExpectedStatus") + @SneakyThrows + void shouldHandleExceptionWithStatus(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) { + var result = doPerformWithError(exceptionClass); + + result.andExpect(status().is(expectedStatus.value())); + } + + @ParameterizedTest + @MethodSource("exceptionAndExpectedStatus") + @SneakyThrows + void shouldRespondWithStatusInBody(Class<? extends Exception> exceptionClass, HttpStatus expectedStatus) { + var result = doPerformWithError(exceptionClass); + + result.andExpect(jsonPath("$.status").value(expectedStatus.value())); + } + + private static Stream<Arguments> exceptionAndExpectedStatus() { + return STATUS_BY_EXCEPTION.entrySet().stream().map(kv -> Arguments.of(kv.getKey(), kv.getValue())); + } + + @SneakyThrows + private ResultActions doPerformWithError(Class<? extends Exception> exceptionClass) { + return mockMvc.perform(get("/test-error").param("errorClassName", exceptionClass.getName())); + } + } + + @DisplayName("ResourceNotFound error") + @Nested + class TestResourceNotFoundError { + + @Test + @SneakyThrows + void shouldHaveStatus() { + var result = doRequestUnknown(); + + result.andExpect(status().is(HttpStatus.NOT_FOUND.value())); + } + + @Test + @SneakyThrows + void shouldRespondWithStatusInBody() { + var result = doRequestUnknown(); + + result.andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())); + } + + @SneakyThrows + private ResultActions doRequestUnknown() { + return mockMvc.perform(get("/api/unknown")); + } + } +}