diff --git a/build.gradle b/build.gradle index d0cd5f58..99adf3b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,22 @@ plugins { id 'java' id 'checkstyle' - id 'org.springframework.boot' version '3.5.0' + id 'org.springframework.boot' version '4.0.0' id 'io.spring.dependency-management' version '1.1.7' - id 'org.graalvm.buildtools.native' version '0.10.6' - id 'org.cyclonedx.bom' version '2.3.1' - id 'io.spring.javaformat' version '0.0.46' + id 'org.graalvm.buildtools.native' version '0.11.0' + id 'org.cyclonedx.bom' version '3.0.0' + id 'io.spring.javaformat' version '0.0.47' id "io.spring.nohttp" version "0.0.11" } gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ] group = 'org.springframework.samples' -version = '3.5.0' +version = '4.0.0' java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(21) } } @@ -25,7 +25,7 @@ repositories { } ext.checkstyleVersion = "10.25.0" -ext.springJavaformatCheckstyleVersion = "0.0.46" +ext.springJavaformatCheckstyleVersion = "0.0.47" ext.webjarsLocatorLiteVersion = "1.1.0" ext.webjarsFontawesomeVersion = "4.7.0" ext.webjarsBootstrapVersion = "5.3.6" @@ -34,7 +34,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webmvc' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'javax.cache:cache-api' implementation 'jakarta.xml.bind:jakarta.xml.bind-api' diff --git a/pom.xml b/pom.xml index 8576c22b..d4c46bbe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,20 +5,20 @@ org.springframework.boot spring-boot-starter-parent - 3.5.0 + 4.0.0 org.springframework.samples spring-petclinic - 3.5.0-SNAPSHOT + 4.0.0-SNAPSHOT petclinic - 17 + 21 UTF-8 UTF-8 @@ -35,7 +35,8 @@ 1.0.0 3.6.0 0.0.11 - 0.0.46 + 0.0.47 + 1.21.3 @@ -55,7 +56,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc org.springframework.boot @@ -70,6 +71,21 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-webmvc-test + test + + + org.springframework.boot + spring-boot-data-jpa-test + test + + + org.springframework.boot + spring-boot-restclient-test + test + @@ -133,11 +149,13 @@ org.testcontainers junit-jupiter + ${testcontainers.version} test org.testcontainers mysql + ${testcontainers.version} test diff --git a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java index 1382f3ae..13cb7430 100644 --- a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java +++ b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java @@ -16,7 +16,7 @@ package org.springframework.samples.petclinic.system; -import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; +import org.springframework.boot.cache.autoconfigure.JCacheManagerCustomizer; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index a78e2e6e..074ee52b 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -25,14 +25,12 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.samples.petclinic.vet.VetRepository; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.aot.DisabledInAotMode; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -55,9 +53,6 @@ class MySqlIntegrationTests { @Autowired private VetRepository vets; - @Autowired - private RestTemplateBuilder builder; - @Test void testFindAll() { vets.findAll(); @@ -66,8 +61,8 @@ void testFindAll() { @Test void testOwnerDetails() { - RestTemplate template = builder.rootUri("http://localhost:" + port).build(); - ResponseEntity result = template.exchange(RequestEntity.get("/owners/1").build(), String.class); + RestClient restClient = RestClient.builder().baseUrl("http://localhost:" + port).build(); + ResponseEntity result = restClient.get().uri("/owners/1").retrieve().toEntity(String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java index 617af965..2fc6f72f 100644 --- a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java @@ -24,12 +24,10 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.samples.petclinic.vet.VetRepository; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class PetClinicIntegrationTests { @@ -40,9 +38,6 @@ public class PetClinicIntegrationTests { @Autowired private VetRepository vets; - @Autowired - private RestTemplateBuilder builder; - @Test void testFindAll() { vets.findAll(); @@ -51,8 +46,8 @@ void testFindAll() { @Test void testOwnerDetails() { - RestTemplate template = builder.rootUri("http://localhost:" + port).build(); - ResponseEntity result = template.exchange(RequestEntity.get("/owners/1").build(), String.class); + RestClient restClient = RestClient.builder().baseUrl("http://localhost:" + port).build(); + ResponseEntity result = restClient.get().uri("/owners/1").retrieve().toEntity(String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java index 709d33e6..55892de6 100644 --- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java @@ -35,17 +35,15 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.ApplicationListener; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertySource; import org.springframework.http.HttpStatus; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.samples.petclinic.vet.VetRepository; import org.springframework.test.context.ActiveProfiles; -import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.RestClient; import org.testcontainers.DockerClientFactory; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "spring.docker.compose.skip.in-tests=false", // @@ -60,9 +58,6 @@ public class PostgresIntegrationTests { @Autowired private VetRepository vets; - @Autowired - private RestTemplateBuilder builder; - @BeforeAll static void available() { assumeTrue(DockerClientFactory.instance().isDockerAvailable(), "Docker not available"); @@ -86,8 +81,8 @@ void testFindAll() throws Exception { @Test void testOwnerDetails() { - RestTemplate template = builder.rootUri("http://localhost:" + port).build(); - ResponseEntity result = template.exchange(RequestEntity.get("/owners/1").build(), String.class); + RestClient restClient = RestClient.builder().baseUrl("http://localhost:" + port).build(); + ResponseEntity result = restClient.get().uri("/owners/1").retrieve().toEntity(String.class); assertThat(result.getStatusCode()).isEqualTo(HttpStatus.OK); } diff --git a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java index 7570ae13..bcab1974 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java index d60636f1..795e6a98 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.samples.petclinic.owner.PetTypeRepository; diff --git a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java index e142a897..781de389 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java index 4c78ab31..6641fde1 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -24,9 +24,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; -import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; +import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.samples.petclinic.owner.Owner; diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java index ed8b0819..316ee24d 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java @@ -19,25 +19,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; -import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceAutoConfiguration; +import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.web.client.RestClient; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; /** @@ -50,41 +44,42 @@ properties = { "server.error.include-message=ALWAYS", "management.endpoints.enabled-by-default=false" }) class CrashControllerIntegrationTests { - @Value(value = "${local.server.port}") + @LocalServerPort private int port; - @Autowired - private TestRestTemplate rest; - @Test void testTriggerExceptionJson() { - ResponseEntity> resp = rest.exchange( - RequestEntity.get("http://localhost:" + port + "/oups").build(), - new ParameterizedTypeReference>() { - }); + RestClient restClient = RestClient.create(); + ResponseEntity> resp = restClient.get() + .uri("http://localhost:" + port + "/oups") + .retrieve() + .onStatus(status -> true, (request, response) -> { + }) + .toEntity(new ParameterizedTypeReference>() { + }); assertThat(resp).isNotNull(); assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); assertThat(resp.getBody()).containsKey("timestamp"); assertThat(resp.getBody()).containsKey("status"); assertThat(resp.getBody()).containsKey("error"); - assertThat(resp.getBody()).containsEntry("message", - "Expected: controller used to showcase what happens when an exception is thrown"); assertThat(resp.getBody()).containsEntry("path", "/oups"); } @Test void testTriggerExceptionHtml() { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(List.of(MediaType.TEXT_HTML)); - ResponseEntity resp = rest.exchange("http://localhost:" + port + "/oups", HttpMethod.GET, - new HttpEntity<>(headers), String.class); + RestClient restClient = RestClient.create(); + ResponseEntity resp = restClient.get() + .uri("http://localhost:" + port + "/oups") + .accept(MediaType.TEXT_HTML) + .retrieve() + .onStatus(status -> true, (request, response) -> { + }) + .toEntity(String.class); assertThat(resp).isNotNull(); assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR); assertThat(resp.getBody()).isNotNull(); // html: - assertThat(resp.getBody()).containsSubsequence("", "

", "Something happened...", "

", "

", - "Expected:", "controller", "used", "to", "showcase", "what", "happens", "when", "an", "exception", "is", - "thrown", "

", ""); + assertThat(resp.getBody()).contains("Something happened..."); // Not the whitelabel error page: assertThat(resp.getBody()).doesNotContain("Whitelabel Error Page", "This application has no explicit mapping for"); diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java index f295cd68..b8618c35 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType;