diff --git a/.github/workflows/dockerImage.yml b/.github/workflows/dockerImage.yml index 0579fc8..d6aa74a 100644 --- a/.github/workflows/dockerImage.yml +++ b/.github/workflows/dockerImage.yml @@ -32,7 +32,6 @@ jobs: java-version: 17.0.7 java-package: jdk architecture: x64 - - name: Caching maven dependencies uses: actions/cache@v1 env: @@ -44,7 +43,7 @@ jobs: run: mvn -B -Pprod clean package -DskipTests - name: Maven Verify run: mvn -B -Pprod clean verify - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: targetfiles path: target/*.jar @@ -75,7 +74,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Download buildfiles artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: targetfiles - name: Get current time @@ -191,3 +190,4 @@ jobs: url: 'https://git.mms-support.de/api/v4/projects/${{ secrets.TSYS_SERVICE_ID }}/repository/tags?ref=master&tag_name=${{ env.DOCKER_IMAGE_TAG }}' method: 'POST' customHeaders: '{"PRIVATE-TOKEN": "${{ secrets.TSYS_ACCESS_TOKEN }}"}' + diff --git a/google_checks_light.xml b/google_checks_light.xml index 2d10a10..7afd3f6 100644 --- a/google_checks_light.xml +++ b/google_checks_light.xml @@ -301,7 +301,7 @@ --> - + diff --git a/pom.xml b/pom.xml index a397423..ad0b006 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 3.0.6 + 3.3.7 @@ -26,9 +26,8 @@ 2.19.0 0.2.3 - 6.0.5 + 6.3.4 6.6.0 - 3.0.0 17 17 2.10.9.2 @@ -58,7 +57,6 @@ org.springframework.security spring-security-core - ${spring-security.version} @@ -121,23 +119,15 @@ jackson-databind-nullable ${jackson-databind-nullable.version} - io.swagger.core.v3 swagger-annotations 2.2.15 - - - io.springfox - springfox-boot-starter - ${springfox-boot-starter.version} - org.hibernate.validator hibernate-validator - ${hibernate.validator.version} @@ -173,7 +163,6 @@ org.apache.commons commons-lang3 - 3.11 org.apache.commons @@ -190,18 +179,15 @@ org.apache.logging.log4j log4j-api - ${log4j.version} org.apache.logging.log4j log4j-to-slf4j - ${log4j.version} org.yaml snakeyaml - 2.0 @@ -220,17 +206,16 @@ org.powermock test 2.0.2 - - - powermock-api-mockito2 - org.powermock - test - 2.0.2 + + + junit + junit + + org.awaitility awaitility - 4.0.3 test @@ -301,14 +286,13 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.6.0 validate validate google_checks_light.xml - UTF-8 true true true @@ -358,6 +342,24 @@ + + org.openrewrite.maven + rewrite-maven-plugin + 5.47.0 + + true + + org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3 + + + + + org.openrewrite.recipe + rewrite-spring + 5.25.0 + + + diff --git a/src/main/java/de/caritas/cob/liveservice/api/config/CustomSwaggerPathWebMvcConfigurer.java b/src/main/java/de/caritas/cob/liveservice/api/config/CustomSwaggerPathWebMvcConfigurer.java deleted file mode 100644 index 4b53f0c..0000000 --- a/src/main/java/de/caritas/cob/liveservice/api/config/CustomSwaggerPathWebMvcConfigurer.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.caritas.cob.liveservice.api.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Component -public class CustomSwaggerPathWebMvcConfigurer implements WebMvcConfigurer { - - @Value("${springfox.docuPath}") - private String docuPath; - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler(docuPath + "/swagger-ui/**") - .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); - registry.addResourceHandler(docuPath + "/**") - .addResourceLocations("classpath:/META-INF/resources/"); - } - -} diff --git a/src/main/java/de/caritas/cob/liveservice/api/config/SpringFoxConfig.java b/src/main/java/de/caritas/cob/liveservice/api/config/SpringFoxConfig.java deleted file mode 100644 index 97c081c..0000000 --- a/src/main/java/de/caritas/cob/liveservice/api/config/SpringFoxConfig.java +++ /dev/null @@ -1,89 +0,0 @@ -package de.caritas.cob.liveservice.api.config; - -import java.time.LocalTime; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.Contact; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; - -/** - * Provides the SpringFox (API documentation generation) configuration. - */ -@Configuration -@Import(BeanValidatorPluginsConfiguration.class) -public class SpringFoxConfig { - - @Value("${springfox.docuTitle}") - private String docuTitle; - @Value("${springfox.docuDescription}") - private String docuDescription; - @Value("${springfox.docuVersion}") - private String docuVersion; - @Value("${springfox.docuTermsUrl}") - private String docuTermsUrl; - @Value("${springfox.docuContactName}") - private String docuContactName; - @Value("${springfox.docuContactUrl}") - private String docuContactUrl; - @Value("${springfox.docuContactEmail}") - private String docuContactEmail; - @Value("${springfox.docuLicense}") - private String docuLicense; - @Value("${springfox.docuLicenseUrl}") - private String docuLicenseUrl; - - // White list for path patterns that should be white listed so that swagger UI can be accessed - // without authorization - public static final String[] WHITE_LIST = - new String[] {"/mails/docs", "/mails/docs/**", "/v2/api-docs", "/configuration/ui", - "/swagger-resources/**", "/configuration/security", "/swagger-ui", "/swagger-ui/**", "/webjars/**"}; - - @Bean - public Docket apiDocket() { - return new Docket(DocumentationType.SWAGGER_2).select() - .apis(RequestHandlerSelectors.basePackage("de.caritas.cob.liveservice.api")).build() - .consumes(getContentTypes()).produces(getContentTypes()).apiInfo(getApiInfo()) - .useDefaultResponseMessages(false).protocols(protocols()) - .directModelSubstitute(LocalTime.class, String.class); - } - - /** - * Returns the API protocols (for documentation). - * - * @return the provided protocols - */ - private Set protocols() { - Set protocols = new HashSet<>(); - protocols.add("https"); - return protocols; - } - - /** - * Returns all content types which should be consumed/produced - */ - private Set getContentTypes() { - Set contentTypes = new HashSet<>(); - contentTypes.add("application/json"); - return contentTypes; - } - - /** - * Returns the API information (defined in application.properties) - * - * @return the static api info - */ - private ApiInfo getApiInfo() { - return new ApiInfo(docuTitle, docuDescription, docuVersion, docuTermsUrl, - new Contact(docuContactName, docuContactUrl, docuContactEmail), docuLicense, docuLicenseUrl, - Collections.emptyList()); - } -} diff --git a/src/main/java/de/caritas/cob/liveservice/api/controller/LiveController.java b/src/main/java/de/caritas/cob/liveservice/api/controller/LiveController.java index 3b9bb11..62e1a68 100644 --- a/src/main/java/de/caritas/cob/liveservice/api/controller/LiveController.java +++ b/src/main/java/de/caritas/cob/liveservice/api/controller/LiveController.java @@ -5,8 +5,8 @@ import de.caritas.cob.liveservice.api.facade.LiveEventFacade; import de.caritas.cob.liveservice.api.model.LiveEventMessage; import de.caritas.cob.liveservice.generated.api.controller.LiveeventApi; -import io.swagger.annotations.Api; -import javax.validation.Valid; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -19,8 +19,8 @@ * Controller for triggering live events. */ @RestController +@Tag(name = "live-controller") @RequiredArgsConstructor -@Api(tags = "live-controller") public class LiveController implements LiveeventApi { private final @NonNull LiveEventFacade liveEventFacade; diff --git a/src/main/java/de/caritas/cob/liveservice/config/security/WebSecurityConfig.java b/src/main/java/de/caritas/cob/liveservice/config/security/WebSecurityConfig.java index 42e491a..2ca9768 100644 --- a/src/main/java/de/caritas/cob/liveservice/config/security/WebSecurityConfig.java +++ b/src/main/java/de/caritas/cob/liveservice/config/security/WebSecurityConfig.java @@ -2,7 +2,6 @@ import de.caritas.cob.liveservice.api.auth.AuthorisationService; import de.caritas.cob.liveservice.api.auth.JwtAuthConverter; -import de.caritas.cob.liveservice.api.config.SpringFoxConfig; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver; import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; @@ -10,7 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; @@ -21,35 +22,43 @@ /** * Configuration class to provide the keycloak security configuration. */ +@Configuration @KeycloakConfiguration public class WebSecurityConfig { + private final AuthorisationService authorisationService; + private final JwtAuthConverterProperties jwtAuthConverterProperties; + @Autowired - AuthorisationService authorisationService; - @Autowired - JwtAuthConverterProperties jwtAuthConverterProperties; + public WebSecurityConfig(AuthorisationService authorisationService, JwtAuthConverterProperties jwtAuthConverterProperties) { + this.authorisationService = authorisationService; + this.jwtAuthConverterProperties = jwtAuthConverterProperties; + } + + protected static final String[] WHITE_LIST = + new String[] {"/mails/docs", "/mails/docs/**", "/v2/api-docs", "/configuration/ui", + "/swagger-resources/**", "/configuration/security", "/swagger-ui", "/swagger-ui/**", "/webjars/**"}; @Bean - public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity - .csrf().disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .sessionAuthenticationStrategy(sessionAuthenticationStrategy()) - .and() - .authorizeRequests() - .requestMatchers(SpringFoxConfig.WHITE_LIST).permitAll() - .requestMatchers(new NegatedRequestMatcher(new AntPathRequestMatcher("/live"))).permitAll() - .requestMatchers(new NegatedRequestMatcher(new AntPathRequestMatcher("/live/**"))) - .permitAll(); + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(management -> management + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .sessionAuthenticationStrategy(sessionAuthenticationStrategy())) + .authorizeHttpRequests(requests -> requests + .requestMatchers(WHITE_LIST).permitAll() + .requestMatchers(new NegatedRequestMatcher(new AntPathRequestMatcher("/live"))).permitAll() + .requestMatchers(new NegatedRequestMatcher(new AntPathRequestMatcher("/live/**"))) + .permitAll()); - httpSecurity.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthConverter()); + httpSecurity.oauth2ResourceServer(server -> server.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter()))); return httpSecurity.build(); } @Bean - public JwtAuthConverter jwtAuthConverter() { + JwtAuthConverter jwtAuthConverter() { return new JwtAuthConverter(jwtAuthConverterProperties, authorisationService); } @@ -64,13 +73,13 @@ protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { * @return the configured {@link KeycloakConfigResolver} */ @Bean - public KeycloakConfigResolver keycloakConfigResolver() { + KeycloakConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Bean @ConfigurationProperties(prefix = "keycloak", ignoreUnknownFields = false) - public KeycloakSpringBootProperties keycloakSpringBootProperties() { + KeycloakSpringBootProperties keycloakSpringBootProperties() { return new KeycloakSpringBootProperties(); } diff --git a/src/main/java/de/caritas/cob/liveservice/websocket/config/WebSocketConfig.java b/src/main/java/de/caritas/cob/liveservice/websocket/config/WebSocketConfig.java index fe0a5e0..1b1211e 100644 --- a/src/main/java/de/caritas/cob/liveservice/websocket/config/WebSocketConfig.java +++ b/src/main/java/de/caritas/cob/liveservice/websocket/config/WebSocketConfig.java @@ -6,10 +6,11 @@ import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @@ -35,7 +36,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker(EVENTS.getSubscriptionEndpoint()) - .setTaskScheduler(new DefaultManagedTaskScheduler()); + .setTaskScheduler(taskScheduler()); } /** @@ -60,4 +61,11 @@ public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(this.clientInboundChannelInterceptor); } + @Bean + public ThreadPoolTaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(10); + scheduler.setThreadNamePrefix("task-scheduler-"); + return scheduler; + } } diff --git a/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompDisconnectHandler.java b/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompDisconnectHandler.java index bf6db3e..cbc4c99 100644 --- a/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompDisconnectHandler.java +++ b/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompDisconnectHandler.java @@ -3,7 +3,6 @@ import static java.util.Objects.requireNonNull; import de.caritas.cob.liveservice.websocket.registry.SocketUserRegistry; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.stereotype.Component; @@ -13,7 +12,6 @@ @Component public class StompDisconnectHandler extends StompRemoveHandler { - @Autowired public StompDisconnectHandler(SocketUserRegistry socketUserRegistry) { super(requireNonNull(socketUserRegistry)); } diff --git a/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompErrorHandler.java b/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompErrorHandler.java index 1c31b2e..7c88325 100644 --- a/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompErrorHandler.java +++ b/src/main/java/de/caritas/cob/liveservice/websocket/stomphandler/StompErrorHandler.java @@ -3,7 +3,6 @@ import static java.util.Objects.requireNonNull; import de.caritas.cob.liveservice.websocket.registry.SocketUserRegistry; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.stereotype.Component; @@ -13,7 +12,6 @@ @Component public class StompErrorHandler extends StompRemoveHandler { - @Autowired public StompErrorHandler(SocketUserRegistry socketUserRegistry) { super(requireNonNull(socketUserRegistry)); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9ebf1d2..93efa17 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,19 +6,6 @@ keycloak.disable-trust-manager=true app.base.url= -# Springfox/API documentation -springfox.docuTitle=Caritas Online Beratung: LiveService -springfox.docuDescription=Provides a REST API service to provide user information and actions. -springfox.docuVersion=0.0.1 -springfox.docuTermsUrl= -springfox.docuContactName= -springfox.docuContactUrl= -springfox.docuContactEmail= -springfox.docuLicense=Apache 2.0 -springfox.docuLicenseUrl=http://www.apache.org/licenses/LICENSE-2.0.html -springfox.docuPath=/liveevent/docs -springfox.documentation.swagger.v2.path=${springfox.docuPath}/v2/api-docs - live.event.retry.send.cron=*/5 * * * * ? live.event.retry.amount=5 live.event.minimum.seconds.before.retry=1 @@ -28,8 +15,8 @@ logging.level.root=WARN management.endpoint.health.enabled=true management.endpoint.health.show-details=never management.endpoints.web.exposure.include=health -management.health.probes.enabled=true +management.endpoint.health.probes.enabled=true spring.security.oauth2.resourceserver.jwt.issuer-uri: https://localhost/auth/realms/onlineberatung spring.security.oauth2.resourceserver.jwt.jwk-set-uri: https://localhost/auth/realms/onlineberatung/protocol/openid-connect/certs spring.jwt.auth.converter.resource-id: app -spring.jwt.auth.converter.principal-attribute: preferred_username \ No newline at end of file +spring.jwt.auth.converter.principal-attribute: preferred_username diff --git a/src/test/java/de/caritas/cob/liveservice/LiveServiceApplicationIT.java b/src/test/java/de/caritas/cob/liveservice/LiveServiceApplicationIT.java index e4b69f0..0828468 100644 --- a/src/test/java/de/caritas/cob/liveservice/LiveServiceApplicationIT.java +++ b/src/test/java/de/caritas/cob/liveservice/LiveServiceApplicationIT.java @@ -29,10 +29,11 @@ import java.util.concurrent.ExecutionException; import org.jeasy.random.EasyRandom; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; 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.messaging.simp.stomp.StompSession; import org.springframework.messaging.simp.stomp.StompSession.Subscription; import org.springframework.test.context.TestPropertySource; @@ -40,6 +41,7 @@ @AutoConfigureMockMvc(addFilters = false) @TestPropertySource(properties = "spring.profiles.active=testing") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) class LiveServiceApplicationIT extends StompClientIntegrationTest { @Autowired diff --git a/src/test/java/de/caritas/cob/liveservice/StompClientIntegrationTest.java b/src/test/java/de/caritas/cob/liveservice/StompClientIntegrationTest.java index 7baeb6e..9410dd0 100644 --- a/src/test/java/de/caritas/cob/liveservice/StompClientIntegrationTest.java +++ b/src/test/java/de/caritas/cob/liveservice/StompClientIntegrationTest.java @@ -18,7 +18,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import lombok.SneakyThrows; -import org.junit.runner.RunWith; import org.keycloak.common.VerificationException; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; @@ -34,7 +33,6 @@ import org.springframework.messaging.simp.stomp.StompSession.Subscription; import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter; import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.web.socket.WebSocketHttpHeaders; import org.springframework.web.socket.client.standard.StandardWebSocketClient; @@ -42,9 +40,8 @@ import org.springframework.web.socket.sockjs.client.SockJsClient; import org.springframework.web.socket.sockjs.client.WebSocketTransport; -@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - classes = StompClientIntegrationTest.TestConfig.class) + classes = {StompClientIntegrationTest.TestConfig.class, TaskSchedulerConfig.class}) public abstract class StompClientIntegrationTest extends AbstractJUnit4SpringContextTests { protected static final String SUBSCRIPTION_ENDPOINT = "/user/events"; @@ -68,7 +65,7 @@ public abstract class StompClientIntegrationTest extends AbstractJUnit4SpringCon public static class TestConfig { @Bean - public KeycloakTokenObserver keycloakTokenObserver() throws VerificationException { + KeycloakTokenObserver keycloakTokenObserver() throws VerificationException { KeycloakTokenObserver observer = mock(KeycloakTokenObserver.class); when(observer.observeUserId(FIRST_VALID_USER)).thenReturn("validated user 1"); when(observer.observeUserId(SECOND_VALID_USER)).thenReturn("validated user 2"); @@ -95,7 +92,7 @@ public Message toMessage(Object o, MessageHeaders messageHeaders) { StompHeaders connectHeaders = new StompHeaders(); connectHeaders.add("accessToken", accessToken); ListenableFuture connect = socketStompClient.connect( - String.format(SOCKET_URL, port), new WebSocketHttpHeaders(), connectHeaders, + SOCKET_URL.formatted(port), new WebSocketHttpHeaders(), connectHeaders, sessionHandler); return connect.get(3, TimeUnit.SECONDS); } diff --git a/src/test/java/de/caritas/cob/liveservice/TaskSchedulerConfig.java b/src/test/java/de/caritas/cob/liveservice/TaskSchedulerConfig.java new file mode 100644 index 0000000..c7a9cdf --- /dev/null +++ b/src/test/java/de/caritas/cob/liveservice/TaskSchedulerConfig.java @@ -0,0 +1,17 @@ +package de.caritas.cob.liveservice; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; + +@Configuration +public class TaskSchedulerConfig { + + @Bean + public ThreadPoolTaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); + scheduler.setPoolSize(10); + scheduler.setThreadNamePrefix("task-scheduler-"); + return scheduler; + } +} diff --git a/src/test/java/de/caritas/cob/liveservice/api/controller/ActuatorControllerIT.java b/src/test/java/de/caritas/cob/liveservice/api/controller/ActuatorControllerIT.java index ff77252..44eecba 100644 --- a/src/test/java/de/caritas/cob/liveservice/api/controller/ActuatorControllerIT.java +++ b/src/test/java/de/caritas/cob/liveservice/api/controller/ActuatorControllerIT.java @@ -1,6 +1,5 @@ package de.caritas.cob.liveservice.api.controller; -import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static org.hamcrest.Matchers.is; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -13,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -35,7 +35,7 @@ public void setup() { @Test void getHealtcheck_Should_returnHealtcheck() throws Exception { mockMvc - .perform(get("/actuator/health").contentType(APPLICATION_JSON)) + .perform(get("/actuator/health").contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("status", is("UP"))); } @@ -43,11 +43,11 @@ void getHealtcheck_Should_returnHealtcheck() throws Exception { @Test void getActuatorEndpoints_Should_returnNotFound_When_ActuatorEndpointsNotExposed() throws Exception { mockMvc - .perform(get("/actuator/env").contentType(APPLICATION_JSON)) + .perform(get("/actuator/env").contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); mockMvc - .perform(get("/actuator/beans").contentType(APPLICATION_JSON)) + .perform(get("/actuator/beans").contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound()); } } diff --git a/src/test/java/de/caritas/cob/liveservice/api/controller/LiveControllerIT.java b/src/test/java/de/caritas/cob/liveservice/api/controller/LiveControllerIT.java index 4be6946..25fd7e0 100644 --- a/src/test/java/de/caritas/cob/liveservice/api/controller/LiveControllerIT.java +++ b/src/test/java/de/caritas/cob/liveservice/api/controller/LiveControllerIT.java @@ -11,15 +11,12 @@ import de.caritas.cob.liveservice.LiveServiceApplication; import de.caritas.cob.liveservice.api.model.VideoCallRequestDTO; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; -@RunWith(SpringRunner.class) @SpringBootTest(classes = LiveServiceApplication.class) @AutoConfigureMockMvc(addFilters = false) public class LiveControllerIT { diff --git a/src/test/java/de/caritas/cob/liveservice/websocket/service/ClientInboundChannelInterceptorTest.java b/src/test/java/de/caritas/cob/liveservice/websocket/service/ClientInboundChannelInterceptorTest.java index 6b3a164..8406097 100644 --- a/src/test/java/de/caritas/cob/liveservice/websocket/service/ClientInboundChannelInterceptorTest.java +++ b/src/test/java/de/caritas/cob/liveservice/websocket/service/ClientInboundChannelInterceptorTest.java @@ -6,19 +6,19 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; import de.caritas.cob.liveservice.websocket.stomphandler.StompHandlerRegistry; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHeaders; @@ -26,9 +26,9 @@ import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.MessageHeaderAccessor; -@RunWith(PowerMockRunner.class) -@PrepareForTest(MessageHeaderAccessor.class) -public class ClientInboundChannelInterceptorTest { +class ClientInboundChannelInterceptorTest { + + private MockedStatic mockedMessageHeaderAccessor; @InjectMocks private ClientInboundChannelInterceptor clientInboundChannelInterceptor; @@ -45,18 +45,25 @@ public class ClientInboundChannelInterceptorTest { @Mock private Message message; - @Before - public void initMocks() { - when(messageHeaders.get(anyString())).thenReturn("header"); - when(message.getHeaders()).thenReturn(messageHeaders); - mockStatic(MessageHeaderAccessor.class); - when(MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) - .thenReturn(stompHeaderAccessor); + @BeforeEach + void setUp() throws Exception { + mockedMessageHeaderAccessor = mockStatic(MessageHeaderAccessor.class); + try (var mocks = MockitoAnnotations.openMocks(this)) { + when(message.getHeaders()).thenReturn(messageHeaders); + when(messageHeaders.get(anyString())).thenReturn("header"); + mockedMessageHeaderAccessor.when(() -> MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) + .thenReturn(stompHeaderAccessor); + } + } + + @AfterEach + void tearDownStaticMocks() { + mockedMessageHeaderAccessor.closeOnDemand(); } @Test - public void preSend_Should_returnUntouchedMessage_When_accessorIsNull() { - when(MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) + void preSend_Should_returnUntouchedMessage_When_accessorIsNull() { + mockedMessageHeaderAccessor.when(() -> MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) .thenReturn(null); var resultMessage = clientInboundChannelInterceptor @@ -66,7 +73,7 @@ public void preSend_Should_returnUntouchedMessage_When_accessorIsNull() { } @Test - public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsConnect() { + void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsConnect() { when(stompHeaderAccessor.getCommand()).thenReturn(StompCommand.CONNECT); clientInboundChannelInterceptor.preSend(message, mock(MessageChannel.class)); @@ -75,7 +82,7 @@ public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandI } @Test - public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsSubscribe() { + void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsSubscribe() { when(stompHeaderAccessor.getCommand()).thenReturn(StompCommand.SUBSCRIBE); clientInboundChannelInterceptor.preSend(message, mock(MessageChannel.class)); @@ -84,7 +91,7 @@ public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandI } @Test - public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsDisconnect() { + void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsDisconnect() { when(stompHeaderAccessor.getCommand()).thenReturn(StompCommand.DISCONNECT); clientInboundChannelInterceptor.preSend(message, mock(MessageChannel.class)); @@ -93,7 +100,7 @@ public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandI } @Test - public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsError() { + void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsError() { when(stompHeaderAccessor.getCommand()).thenReturn(StompCommand.ERROR); clientInboundChannelInterceptor.preSend(message, mock(MessageChannel.class)); @@ -102,7 +109,7 @@ public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandI } @Test - public void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsAck() { + void preSend_Should_callRegistryWithExpectedCommand_When_accessorCommandIsAck() { when(stompHeaderAccessor.getCommand()).thenReturn(StompCommand.ACK); clientInboundChannelInterceptor.preSend(message, mock(MessageChannel.class)); diff --git a/src/test/java/de/caritas/cob/liveservice/websocket/service/KeycloakTokenObserverTest.java b/src/test/java/de/caritas/cob/liveservice/websocket/service/KeycloakTokenObserverTest.java index 1b064c9..a48f4e6 100644 --- a/src/test/java/de/caritas/cob/liveservice/websocket/service/KeycloakTokenObserverTest.java +++ b/src/test/java/de/caritas/cob/liveservice/websocket/service/KeycloakTokenObserverTest.java @@ -2,12 +2,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.mockito.Mockito.mockStatic; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.rotation.AdapterTokenVerifier; import org.keycloak.adapters.springboot.KeycloakSpringBootProperties; @@ -15,12 +16,14 @@ import org.keycloak.representations.AccessToken; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; -@RunWith(PowerMockRunner.class) -@PrepareForTest({AdapterTokenVerifier.class, KeycloakDeploymentBuilder.class}) -public class KeycloakTokenObserverTest { +class KeycloakTokenObserverTest { + + private MockedStatic mockedKeycloakDeploymentBuilder; + + private MockedStatic mockedAdapterTokenVerifier; @InjectMocks private KeycloakTokenObserver keycloakTokenObserver; @@ -28,26 +31,36 @@ public class KeycloakTokenObserverTest { @Mock private KeycloakSpringBootProperties keycloakSpringBootProperties; - @Test(expected = VerificationException.class) - public void observeUserId_Should_throwVerificationException_When_tokenIsNull() - throws VerificationException { - this.keycloakTokenObserver.observeUserId(null); + @BeforeEach + void setUpStaticMocks() throws Exception { + try (var mocks = MockitoAnnotations.openMocks(this)) { + mockedKeycloakDeploymentBuilder = mockStatic(KeycloakDeploymentBuilder.class); + mockedAdapterTokenVerifier = mockStatic(AdapterTokenVerifier.class); + } } - @Test(expected = VerificationException.class) - public void observeUserId_Should_throwVerificationException_When_tokenIsEmpty() - throws VerificationException { - this.keycloakTokenObserver.observeUserId(""); + @AfterEach + void tearDownStaticMocks() { + mockedAdapterTokenVerifier.closeOnDemand(); + mockedKeycloakDeploymentBuilder.closeOnDemand(); + } + + @Test + void observeUserId_Should_throwVerificationException_When_tokenIsNull() { + assertThrows(VerificationException.class, () -> this.keycloakTokenObserver.observeUserId(null)); + } + + @Test + void observeUserId_Should_throwVerificationException_When_tokenIsEmpty() { + assertThrows(VerificationException.class, () -> this.keycloakTokenObserver.observeUserId("")); } @Test - public void observeUserId_Should_returnUserId_When_tokenIsValid() + void observeUserId_Should_returnUserId_When_tokenIsValid() throws VerificationException { - mockStatic(KeycloakDeploymentBuilder.class); - mockStatic(AdapterTokenVerifier.class); AccessToken accessToken = new AccessToken(); accessToken.setOtherClaims("userId", "validId"); - when(AdapterTokenVerifier.verifyToken(any(), any())).thenReturn(accessToken); + mockedAdapterTokenVerifier.when(() -> AdapterTokenVerifier.verifyToken(any(), any())).thenReturn(accessToken); String userId = this.keycloakTokenObserver.observeUserId("valid token"); assertThat(userId, is("validId")); diff --git a/src/test/java/de/caritas/cob/liveservice/websocket/service/WebSocketSessionIdResolverTest.java b/src/test/java/de/caritas/cob/liveservice/websocket/service/WebSocketSessionIdResolverTest.java index 226657a..b4933b2 100644 --- a/src/test/java/de/caritas/cob/liveservice/websocket/service/WebSocketSessionIdResolverTest.java +++ b/src/test/java/de/caritas/cob/liveservice/websocket/service/WebSocketSessionIdResolverTest.java @@ -11,13 +11,13 @@ import de.caritas.cob.liveservice.websocket.model.WebSocketUserSession; import de.caritas.cob.liveservice.websocket.registry.SocketUserRegistry; import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class WebSocketSessionIdResolverTest { @InjectMocks diff --git a/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompAcknowledgeHandlerTest.java b/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompAcknowledgeHandlerTest.java index 44ee3a4..7a43806 100644 --- a/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompAcknowledgeHandlerTest.java +++ b/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompAcknowledgeHandlerTest.java @@ -5,29 +5,29 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; import de.caritas.cob.liveservice.websocket.registry.LiveEventMessageQueue; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.MessageHeaderAccessor; -@RunWith(PowerMockRunner.class) -@PrepareForTest(MessageHeaderAccessor.class) -public class StompAcknowledgeHandlerTest { +class StompAcknowledgeHandlerTest { + + private MockedStatic mockedMessageHeaderAccessor; @InjectMocks private StompAcknowledgeHandler stompAcknowledgeHandler; @@ -44,31 +44,39 @@ public class StompAcknowledgeHandlerTest { @Mock private Message message; - @Before - public void initMocks() { - when(messageHeaders.get(anyString())).thenReturn("header"); - when(message.getHeaders()).thenReturn(messageHeaders); - mockStatic(MessageHeaderAccessor.class); - when(MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) - .thenReturn(stompHeaderAccessor); + @BeforeEach + void setup() throws Exception { + mockedMessageHeaderAccessor = mockStatic(MessageHeaderAccessor.class); + try (var mocks = MockitoAnnotations.openMocks(this)) { + when(messageHeaders.get(anyString())).thenReturn("header"); + when(message.getHeaders()).thenReturn(messageHeaders); + mockedMessageHeaderAccessor.when(() -> MessageHeaderAccessor.getAccessor(any(Message.class), + eq(StompHeaderAccessor.class))) + .thenReturn(stompHeaderAccessor); + } + } + + @AfterEach + void tearDownStaticMocks() { + mockedMessageHeaderAccessor.closeOnDemand(); } @Test - public void supportedStompCommand_Should_returnAck() { + void supportedStompCommand_Should_returnAck() { var command = this.stompAcknowledgeHandler.supportedStompCommand(); assertThat(command, is(StompCommand.ACK)); } @Test - public void handle_Should_useNoServices_When_messageIsNull() { + void handle_Should_useNoServices_When_messageIsNull() { this.stompAcknowledgeHandler.handle(null); verifyNoInteractions(this.liveEventMessageQueue); } @Test - public void handle_Should_removeQueuedMessageWithId_When_messageHasMessageId() { + void handle_Should_removeQueuedMessageWithId_When_messageHasMessageId() { when(this.stompHeaderAccessor.getFirstNativeHeader(anyString())).thenReturn("id"); this.stompAcknowledgeHandler.handle(this.message); diff --git a/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompConnectHandlerTest.java b/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompConnectHandlerTest.java index b20429d..e4c2eb7 100644 --- a/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompConnectHandlerTest.java +++ b/src/test/java/de/caritas/cob/liveservice/websocket/stomphandler/StompConnectHandlerTest.java @@ -2,35 +2,36 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.mockStatic; import de.caritas.cob.liveservice.websocket.exception.InvalidAccessTokenException; import de.caritas.cob.liveservice.websocket.registry.SocketUserRegistry; import de.caritas.cob.liveservice.websocket.service.KeycloakTokenObserver; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.keycloak.common.VerificationException; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.MockedStatic; +import org.mockito.MockitoAnnotations; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.MessageHeaderAccessor; -@RunWith(PowerMockRunner.class) -@PrepareForTest(MessageHeaderAccessor.class) -public class StompConnectHandlerTest { +class StompConnectHandlerTest { + + private MockedStatic mockedMessageHeaderAccessor; @InjectMocks private StompConnectHandler stompConnectHandler; @@ -50,41 +51,49 @@ public class StompConnectHandlerTest { @Mock private Message message; - @Before - public void initMocks() { - when(messageHeaders.get(anyString())).thenReturn("header"); - when(message.getHeaders()).thenReturn(messageHeaders); - mockStatic(MessageHeaderAccessor.class); - when(MessageHeaderAccessor.getAccessor(any(Message.class), eq(StompHeaderAccessor.class))) - .thenReturn(stompHeaderAccessor); + @BeforeEach + void setUp() throws Exception { + mockedMessageHeaderAccessor = mockStatic(MessageHeaderAccessor.class); + try (var mocks = MockitoAnnotations.openMocks(this)) { + when(messageHeaders.get(anyString())).thenReturn("header"); + when(message.getHeaders()).thenReturn(messageHeaders); + mockedMessageHeaderAccessor.when(() -> MessageHeaderAccessor.getAccessor(any(Message.class), + eq(StompHeaderAccessor.class))) + .thenReturn(stompHeaderAccessor); + } + } + + @AfterEach + void tearDownStaticMocks() { + mockedMessageHeaderAccessor.closeOnDemand(); } @Test - public void supportedStompCommand_Should_returnConnect() { + void supportedStompCommand_Should_returnConnect() { var command = this.stompConnectHandler.supportedStompCommand(); assertThat(command, is(StompCommand.CONNECT)); } @Test - public void handle_Should_useNoServices_When_messageIsNull() { + void handle_Should_useNoServices_When_messageIsNull() { this.stompConnectHandler.handle(null); verifyNoInteractions(this.keycloakTokenObserver, this.socketUserRegistry); } - @Test(expected = InvalidAccessTokenException.class) - public void handle_Should_throwInvalidAccessTokenException_When_tokenIsInvalid() + @Test + void handle_Should_throwInvalidAccessTokenException_When_tokenIsInvalid() throws VerificationException { when(this.stompHeaderAccessor.getFirstNativeHeader(anyString())).thenReturn("accessToken"); when(this.keycloakTokenObserver.observeUserId(anyString())) .thenThrow(new VerificationException()); - this.stompConnectHandler.handle(this.message); + assertThrows(InvalidAccessTokenException.class, () -> this.stompConnectHandler.handle(this.message)); } @Test - public void handle_Should_useAllServices_When_tokenIsValid() throws VerificationException { + void handle_Should_useAllServices_When_tokenIsValid() throws VerificationException { when(this.stompHeaderAccessor.getFirstNativeHeader(anyString())).thenReturn("accessToken"); this.stompConnectHandler.handle(this.message);