From 74c8b372ebf491b28a7d1d0425fe2aa7badbe7bb Mon Sep 17 00:00:00 2001 From: andreatp Date: Mon, 2 Mar 2026 14:02:36 +0000 Subject: [PATCH] spring example --- README.md | 10 +- .../spring-boot-pet-clinic/invoker.properties | 1 + it/src/it/spring-boot-pet-clinic/pom.xml | 57 +++++++ .../src/main/java/org/acme/Application.java | 12 ++ .../src/main/java/org/acme/Pet.java | 50 ++++++ .../src/main/java/org/acme/PetController.java | 64 ++++++++ .../src/main/java/org/acme/PetRepository.java | 9 ++ .../src/main/resources/application.properties | 8 + .../test/java/org/acme/PetControllerTest.java | 147 ++++++++++++++++++ 9 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 it/src/it/spring-boot-pet-clinic/invoker.properties create mode 100644 it/src/it/spring-boot-pet-clinic/pom.xml create mode 100644 it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Application.java create mode 100644 it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Pet.java create mode 100644 it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetController.java create mode 100644 it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetRepository.java create mode 100644 it/src/it/spring-boot-pet-clinic/src/main/resources/application.properties create mode 100644 it/src/it/spring-boot-pet-clinic/src/test/java/org/acme/PetControllerTest.java diff --git a/README.md b/README.md index 7d77e94..2af0c8e 100644 --- a/README.md +++ b/README.md @@ -70,14 +70,22 @@ quarkus.datasource.password=password quarkus.datasource.jdbc.min-size=1 quarkus.datasource.jdbc.max-size=1 quarkus.devservices.enabled=false +quarkus.hibernate-orm.dialect=org.hibernate.dialect.PostgreSQLDialect +quarkus.hibernate-orm.unsupported-properties."hibernate.boot.allow_jdbc_metadata_access"=false ``` -### Spring Boot - NOT TESTED +### Spring Boot ```properties # application-test.properties spring.datasource.url=jdbc:pglite:memory:// spring.datasource.driver-class-name=io.roastedroot.pglite4j.jdbc.PgLiteDriver +spring.datasource.username=postgres +spring.datasource.password=password +spring.datasource.hikari.maximum-pool-size=1 +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.boot.allow_jdbc_metadata_access=false ``` ### HikariCP - NOT TESTED diff --git a/it/src/it/spring-boot-pet-clinic/invoker.properties b/it/src/it/spring-boot-pet-clinic/invoker.properties new file mode 100644 index 0000000..84099bc --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/invoker.properties @@ -0,0 +1 @@ +invoker.goals=test diff --git a/it/src/it/spring-boot-pet-clinic/pom.xml b/it/src/it/spring-boot-pet-clinic/pom.xml new file mode 100644 index 0000000..fb4e2ea --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 3.4.4 + + + + org.acme + spring-boot-pet-clinic-it + 0.0-SNAPSHOT + jar + + + 17 + UTF-8 + + + + + io.roastedroot + pglite4j-jdbc + @project.version@ + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + io.rest-assured + rest-assured + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Application.java b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Application.java new file mode 100644 index 0000000..01e439b --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Application.java @@ -0,0 +1,12 @@ +package org.acme; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Pet.java b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Pet.java new file mode 100644 index 0000000..2067c60 --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/Pet.java @@ -0,0 +1,50 @@ +package org.acme; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class Pet { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private String species; + private int age; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSpecies() { + return species; + } + + public void setSpecies(String species) { + this.species = species; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetController.java b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetController.java new file mode 100644 index 0000000..2aa4b77 --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetController.java @@ -0,0 +1,64 @@ +package org.acme; + +import java.util.List; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +@RestController +@RequestMapping("/pets") +public class PetController { + + private final PetRepository repository; + + public PetController(PetRepository repository) { + this.repository = repository; + } + + @GetMapping + public List list() { + return repository.findAllByOrderByIdAsc(); + } + + @GetMapping("/{id}") + public Pet get(@PathVariable Long id) { + return repository + .findById(id) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Pet create(@RequestBody Pet pet) { + return repository.save(pet); + } + + @PutMapping("/{id}") + public Pet update(@PathVariable Long id, @RequestBody Pet pet) { + Pet existing = + repository + .findById(id) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + existing.setName(pet.getName()); + existing.setSpecies(pet.getSpecies()); + existing.setAge(pet.getAge()); + return repository.save(existing); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long id) { + if (!repository.existsById(id)) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); + } + repository.deleteById(id); + } +} diff --git a/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetRepository.java b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetRepository.java new file mode 100644 index 0000000..d137f5e --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/main/java/org/acme/PetRepository.java @@ -0,0 +1,9 @@ +package org.acme; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface PetRepository extends JpaRepository { + + List findAllByOrderByIdAsc(); +} diff --git a/it/src/it/spring-boot-pet-clinic/src/main/resources/application.properties b/it/src/it/spring-boot-pet-clinic/src/main/resources/application.properties new file mode 100644 index 0000000..3c903d9 --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/main/resources/application.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:pglite:memory:// +spring.datasource.driver-class-name=io.roastedroot.pglite4j.jdbc.PgLiteDriver +spring.datasource.username=postgres +spring.datasource.password=password +spring.datasource.hikari.maximum-pool-size=1 +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.boot.allow_jdbc_metadata_access=false diff --git a/it/src/it/spring-boot-pet-clinic/src/test/java/org/acme/PetControllerTest.java b/it/src/it/spring-boot-pet-clinic/src/test/java/org/acme/PetControllerTest.java new file mode 100644 index 0000000..ed64875 --- /dev/null +++ b/it/src/it/spring-boot-pet-clinic/src/test/java/org/acme/PetControllerTest.java @@ -0,0 +1,147 @@ +package org.acme; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class PetControllerTest { + + @LocalServerPort private int port; + + private int buddyId; + private int whiskersId; + private int goldieId; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } + + @Test + @Order(1) + void createBuddy() { + buddyId = + given().contentType(ContentType.JSON) + .body("{\"name\":\"Buddy\",\"species\":\"Dog\",\"age\":3}") + .when() + .post("/pets") + .then() + .statusCode(201) + .body("name", is("Buddy")) + .body("species", is("Dog")) + .body("age", is(3)) + .extract() + .path("id"); + } + + @Test + @Order(2) + void createWhiskers() { + whiskersId = + given().contentType(ContentType.JSON) + .body("{\"name\":\"Whiskers\",\"species\":\"Cat\",\"age\":5}") + .when() + .post("/pets") + .then() + .statusCode(201) + .body("name", is("Whiskers")) + .extract() + .path("id"); + } + + @Test + @Order(3) + void createGoldie() { + goldieId = + given().contentType(ContentType.JSON) + .body("{\"name\":\"Goldie\",\"species\":\"Fish\",\"age\":1}") + .when() + .post("/pets") + .then() + .statusCode(201) + .body("name", is("Goldie")) + .extract() + .path("id"); + } + + @Test + @Order(4) + void listAll() { + given().when() + .get("/pets") + .then() + .statusCode(200) + .body("size()", is(3)) + .body("[0].name", is("Buddy")) + .body("[1].name", is("Whiskers")) + .body("[2].name", is("Goldie")); + } + + @Test + @Order(5) + void getById() { + given().when() + .get("/pets/" + buddyId) + .then() + .statusCode(200) + .body("name", is("Buddy")) + .body("species", is("Dog")) + .body("age", is(3)); + } + + @Test + @Order(6) + void updateBuddy() { + given().contentType(ContentType.JSON) + .body("{\"name\":\"Buddy\",\"species\":\"Dog\",\"age\":4}") + .when() + .put("/pets/" + buddyId) + .then() + .statusCode(200) + .body("name", is("Buddy")) + .body("age", is(4)); + } + + @Test + @Order(7) + void verifyUpdate() { + given().when().get("/pets/" + buddyId).then().statusCode(200).body("age", is(4)); + } + + @Test + @Order(8) + void deleteGoldie() { + given().when().delete("/pets/" + goldieId).then().statusCode(204); + } + + @Test + @Order(9) + void listAfterDelete() { + given().when() + .get("/pets") + .then() + .statusCode(200) + .body("size()", is(2)) + .body("[0].name", is("Buddy")) + .body("[1].name", is("Whiskers")); + } + + @Test + @Order(10) + void getDeletedReturns404() { + given().when().get("/pets/" + goldieId).then().statusCode(404); + } +}