diff --git a/src.zip b/src.zip deleted file mode 100644 index 8f9fc4907..000000000 Binary files a/src.zip and /dev/null differ diff --git a/src/main/java/org/springframework/samples/petclinic/owner/controller/PetAttributesController.java b/src/main/java/org/springframework/samples/petclinic/owner/controller/PetAttributesController.java new file mode 100644 index 000000000..b6813a3d0 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/controller/PetAttributesController.java @@ -0,0 +1,67 @@ +package org.springframework.samples.petclinic.owner.controller; + +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO; +import org.springframework.samples.petclinic.owner.expection.PetNotFoundException; +import org.springframework.samples.petclinic.owner.model.PetAttributes; +import org.springframework.samples.petclinic.owner.repository.PetRepository; +import org.springframework.samples.petclinic.owner.service.PetAttributesService; +import org.springframework.samples.petclinic.owner.validation.PetIdExistsValidator; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@RestController +@RequestMapping("/pets/{petId}/attributes") +public class PetAttributesController { + + private final PetAttributesService petAttributesService; + + private PetIdExistsValidator petIdExistsValidator; + + private PetRepository petRepository; + + public PetAttributesController(PetIdExistsValidator petIdExistsValidator, PetAttributesService petAttributesService, + PetRepository petRepository) { + this.petIdExistsValidator = petIdExistsValidator; + this.petAttributesService = petAttributesService; + this.petRepository = petRepository; + } + + @GetMapping + public ResponseEntity getPetAttributes(@PathVariable("petId") int petId) { + if (!petRepository.existsById(petId)) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Pet not found"); + } + + Optional optionalAttributes = petAttributesService.findByPetId(petId); + + return optionalAttributes.>map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).body("Attributes not found")); + } + + @PostMapping + public ResponseEntity savePetAttributes(@PathVariable int petId, @Valid @RequestBody PetAttributesDTO dto, + BindingResult bindingResult) { + // Validate petId exists + petIdExistsValidator.validate(petId, bindingResult); + + if (bindingResult.hasErrors()) { + return ResponseEntity.badRequest().body(bindingResult.getAllErrors()); + } + + dto.setPetId(petId); + + try { + petAttributesService.savePetAttributes(dto); + return ResponseEntity.status(HttpStatus.CREATED).body("Pet attributes saved"); + } catch (PetNotFoundException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/controller/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/controller/PetController.java index 7b3e1bf54..63df354f2 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/controller/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/controller/PetController.java @@ -72,7 +72,7 @@ public class PetController { @ModelAttribute("pet") public Pet findPet(@PathVariable("ownerId") int ownerId, - @PathVariable(name = "petId", required = false) Integer petId) { + @PathVariable(name = "petId", required = false) Integer petId) { if (petId == null) { return new Pet(); diff --git a/src/main/java/org/springframework/samples/petclinic/owner/controller/VisitController.java b/src/main/java/org/springframework/samples/petclinic/owner/controller/VisitController.java index 43ef4a99f..072dfcc51 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/controller/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/controller/VisitController.java @@ -65,7 +65,7 @@ public class VisitController { */ @ModelAttribute("visit") public Visit loadPetWithVisit(@PathVariable("ownerId") int ownerId, @PathVariable("petId") int petId, - Map model) { + Map model) { Optional optionalOwner = owners.findById(ownerId); Owner owner = optionalOwner.orElseThrow(() -> new IllegalArgumentException( "Owner not found with id: " + ownerId + ". Please ensure the ID is correct ")); diff --git a/src/main/java/org/springframework/samples/petclinic/owner/dto/PetAttributesDTO.java b/src/main/java/org/springframework/samples/petclinic/owner/dto/PetAttributesDTO.java new file mode 100644 index 000000000..a619c631d --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/dto/PetAttributesDTO.java @@ -0,0 +1,60 @@ +package org.springframework.samples.petclinic.owner.dto; + +import jakarta.validation.constraints.*; + +import java.math.BigDecimal; + +public class PetAttributesDTO { + + @NotNull(message = "Pet ID is required") + private int petId; + + @NotBlank(message = "Temperament is required") + @Size(max = 100, message = "Temperament must not exceed 100 characters") + private String temperament; + + @NotNull + @DecimalMin(value = "0.1", inclusive = true, message = "Length must be at least 0.1 cm") + @DecimalMax(value = "500.0", inclusive = true, message = "Length must be less than or equal to 500 cm") + private BigDecimal lengthCm; + + @NotNull + @DecimalMin(value = "0.1", inclusive = true, message = "Weight must be at least 0.1 kg") + @DecimalMax(value = "500.0", inclusive = true, message = "Weight must be less than or equal to 500 kg") + private BigDecimal weightKg; + + // Getters and setters + + public int getPetId() { + return petId; + } + + public void setPetId(int petId) { + this.petId = petId; + } + + public String getTemperament() { + return temperament; + } + + public void setTemperament(String temperament) { + this.temperament = temperament; + } + + public BigDecimal getLengthCm() { + return lengthCm; + } + + public void setLengthCm(BigDecimal lengthCm) { + this.lengthCm = lengthCm; + } + + public BigDecimal getWeightKg() { + return weightKg; + } + + public void setWeightKg(BigDecimal weightKg) { + this.weightKg = weightKg; + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/expection/PetNotFoundException.java b/src/main/java/org/springframework/samples/petclinic/owner/expection/PetNotFoundException.java new file mode 100644 index 000000000..a1746f5a2 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/expection/PetNotFoundException.java @@ -0,0 +1,9 @@ +package org.springframework.samples.petclinic.owner.expection; + +public class PetNotFoundException extends RuntimeException { + + public PetNotFoundException(String message) { + super(message); + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/model/PetAttributes.java b/src/main/java/org/springframework/samples/petclinic/owner/model/PetAttributes.java new file mode 100644 index 000000000..69f9204d2 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/model/PetAttributes.java @@ -0,0 +1,82 @@ +package org.springframework.samples.petclinic.owner.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; + +import java.math.BigDecimal; + +@Entity +@Table(name = "pet_attributes") +public class PetAttributes { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "pet_id", nullable = false) + @JsonIgnore + private Pet pet; + + private String temperament; + + @Column(name = "length_cm", precision = 5, scale = 2) + private BigDecimal lengthCm; + + @Column(name = "weight_kg", precision = 5, scale = 2) + private BigDecimal weightKg; + + @Column(name = "additional_attributes", columnDefinition = "json") + private String additionalAttributes; + + // --- Getters and Setters --- + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Pet getPet() { + return pet; + } + + public void setPet(Pet pet) { + this.pet = pet; + } + + public String getTemperament() { + return temperament; + } + + public void setTemperament(String temperament) { + this.temperament = temperament; + } + + public BigDecimal getLengthCm() { + return lengthCm; + } + + public void setLengthCm(BigDecimal lengthCm) { + this.lengthCm = lengthCm; + } + + public BigDecimal getWeightKg() { + return weightKg; + } + + public void setWeightKg(BigDecimal weightKg) { + this.weightKg = weightKg; + } + + public String getAdditionalAttributes() { + return additionalAttributes; + } + + public void setAdditionalAttributes(String additionalAttributes) { + this.additionalAttributes = additionalAttributes; + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/repository/PetAttributesRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/repository/PetAttributesRepository.java new file mode 100644 index 000000000..f49221593 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/repository/PetAttributesRepository.java @@ -0,0 +1,24 @@ +package org.springframework.samples.petclinic.owner.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.samples.petclinic.owner.model.PetAttributes; + +/** + * Repository class for PetAttributes domain objects. Provides CRUD + * operations and custom query methods related to a pet's attributes. + * + * @see org.springframework.samples.petclinic.owner.model.PetAttributes + */ +public interface PetAttributesRepository extends JpaRepository { + + /** + * Find pet attributes by pet ID. + * + * @param petId the ID of the pet + * @return an Optional containing the PetAttributes if found + */ + Optional findByPetId(Integer petId); + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/repository/PetRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/repository/PetRepository.java new file mode 100644 index 000000000..98c6f5c89 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/repository/PetRepository.java @@ -0,0 +1,8 @@ +package org.springframework.samples.petclinic.owner.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.samples.petclinic.owner.model.Pet; + +public interface PetRepository extends JpaRepository { + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/service/PetAttributesService.java b/src/main/java/org/springframework/samples/petclinic/owner/service/PetAttributesService.java new file mode 100644 index 000000000..8465a95c8 --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/service/PetAttributesService.java @@ -0,0 +1,47 @@ +package org.springframework.samples.petclinic.owner.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO; +import org.springframework.samples.petclinic.owner.expection.PetNotFoundException; +import org.springframework.samples.petclinic.owner.model.Pet; +import org.springframework.samples.petclinic.owner.model.PetAttributes; +import org.springframework.samples.petclinic.owner.repository.PetAttributesRepository; +import org.springframework.samples.petclinic.owner.repository.PetRepository; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public class PetAttributesService { + + private final PetAttributesRepository petAttributesRepository; + + private final PetRepository petRepository; + + @Autowired + public PetAttributesService(PetAttributesRepository petAttributesRepository, PetRepository petRepository) { + this.petAttributesRepository = petAttributesRepository; + this.petRepository = petRepository; + } + + public Optional findByPetId(Integer petId) { + return petAttributesRepository.findByPetId(petId); + } + + public void savePetAttributes(PetAttributesDTO dto) { + Optional pet = petRepository.findById(dto.getPetId()); + + if (pet.isEmpty()) { + throw new PetNotFoundException("Pet not found"); + } + + PetAttributes attributes = petAttributesRepository.findByPetId(dto.getPetId()).orElse(new PetAttributes()); + + attributes.setPet(pet.get()); + attributes.setTemperament(dto.getTemperament()); + attributes.setLengthCm(dto.getLengthCm()); + attributes.setWeightKg(dto.getWeightKg()); + petAttributesRepository.save(attributes); + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/validation/PetIdExistsValidator.java b/src/main/java/org/springframework/samples/petclinic/owner/validation/PetIdExistsValidator.java new file mode 100644 index 000000000..84462979e --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/validation/PetIdExistsValidator.java @@ -0,0 +1,49 @@ +package org.springframework.samples.petclinic.owner.validation; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.owner.repository.PetRepository; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +@Component +public class PetIdExistsValidator implements Validator { + + private final PetRepository petRepository; + + @Autowired + public PetIdExistsValidator(PetRepository petRepository) { + this.petRepository = petRepository; + } + + @Override + public boolean supports(Class clazz) { + // This validator supports Integer class, for validating petId field + return Integer.class.equals(clazz); + } + + @Override + public void validate(Object target, Errors errors) { + if (target == null) { + errors.reject("petId.null", "Pet ID is required"); + return; + } + + if (!(target instanceof Integer)) { + errors.reject("petId.type", "Pet ID must be an integer"); + return; + } + + Integer petId = (Integer) target; + + if (petId <= 0) { + errors.rejectValue("", "petId.positive", "Pet ID must be a positive number"); + return; + } + + if (!petRepository.existsById(petId)) { + errors.rejectValue("", "petId.notFound", "Pet with ID " + petId + " does not exist"); + } + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6d677b6e6..af0c0c9be 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver database=mysql spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic} spring.datasource.username=root -spring.datasource.password=testA@123 +spring.datasource.password=your-password # SQL is written to be idempotent so this is safe spring.sql.init.mode=always diff --git a/src/main/resources/db/mysql/schema.sql b/src/main/resources/db/mysql/schema.sql index 2591a516d..8fe95dce9 100644 --- a/src/main/resources/db/mysql/schema.sql +++ b/src/main/resources/db/mysql/schema.sql @@ -53,3 +53,13 @@ CREATE TABLE IF NOT EXISTS visits ( description VARCHAR(255), FOREIGN KEY (pet_id) REFERENCES pets(id) ) engine=InnoDB; + +CREATE TABLE IF NOT EXISTS pet_attributes ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + pet_id INT UNSIGNED NOT NULL, + temperament VARCHAR(100), + length_cm DECIMAL(5,2), + weight_kg DECIMAL(5,2), + additional_attributes JSON, + FOREIGN KEY (pet_id) REFERENCES pets(id) + ); diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetAttributesControllerTest.java b/src/test/java/org/springframework/samples/petclinic/owner/PetAttributesControllerTest.java new file mode 100644 index 000000000..cbaea8dac --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetAttributesControllerTest.java @@ -0,0 +1,139 @@ +package org.springframework.samples.petclinic.owner; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.samples.petclinic.owner.controller.PetAttributesController; +import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO; +import org.springframework.samples.petclinic.owner.expection.PetNotFoundException; +import org.springframework.samples.petclinic.owner.model.PetAttributes; +import org.springframework.samples.petclinic.owner.repository.PetRepository; +import org.springframework.samples.petclinic.owner.service.PetAttributesService; +import org.springframework.samples.petclinic.owner.validation.PetIdExistsValidator; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.validation.BindingResult; + +import java.math.BigDecimal; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@WebMvcTest(PetAttributesController.class) +public class PetAttributesControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private PetAttributesService petAttributesService; + + @MockBean + private PetIdExistsValidator petIdExistsValidator; + + @MockBean + private PetRepository petRepository; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void testGetPetAttributes_Found() throws Exception { + int petId = 1; + PetAttributes attributes = new PetAttributes(); + + Mockito.when(petRepository.existsById(petId)).thenReturn(true); + Mockito.when(petAttributesService.findByPetId(petId)).thenReturn(Optional.of(attributes)); + + mockMvc.perform(get("/pets/{petId}/attributes", petId)).andExpect(status().isOk()); + } + + @Test + void testGetPetAttributes_PetNotFound() throws Exception { + int petId = 1; + + Mockito.when(petRepository.existsById(petId)).thenReturn(false); + + mockMvc.perform(get("/pets/{petId}/attributes", petId)) + .andExpect(status().isNotFound()) + .andExpect(content().string("Pet not found")); + } + + @Test + void testGetPetAttributes_AttributesNotFound() throws Exception { + int petId = 1; + + Mockito.when(petRepository.existsById(petId)).thenReturn(true); + Mockito.when(petAttributesService.findByPetId(petId)).thenReturn(Optional.empty()); + + mockMvc.perform(get("/pets/{petId}/attributes", petId)) + .andExpect(status().isNotFound()) + .andExpect(content().string("Attributes not found")); + } + + @Test + void testSavePetAttributes_Success() throws Exception { + int petId = 1; + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setTemperament("Calm"); + dto.setWeightKg(BigDecimal.valueOf(12.5)); + dto.setLengthCm(BigDecimal.valueOf(60.0)); + + Mockito.doNothing().when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class)); + Mockito.doNothing().when(petAttributesService).savePetAttributes(any(PetAttributesDTO.class)); + + mockMvc + .perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) + .andExpect(status().isCreated()) + .andExpect(content().string("Pet attributes saved")); + } + + @Test + void testSavePetAttributes_PetNotFoundException() throws Exception { + int petId = 1; + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setTemperament("Aggressive"); + dto.setWeightKg(BigDecimal.valueOf(12.5)); + dto.setLengthCm(BigDecimal.valueOf(60.0)); + + Mockito.doNothing().when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class)); + Mockito.doThrow(new PetNotFoundException("Pet not found")) + .when(petAttributesService) + .savePetAttributes(any(PetAttributesDTO.class)); + + mockMvc + .perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) + .andExpect(status().isNotFound()) + .andExpect(content().string("Pet not found")); + } + + @Test + void testSavePetAttributes_ValidationErrors() throws Exception { + int petId = 1; + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setTemperament("Friendly"); + dto.setWeightKg(BigDecimal.valueOf(10.0)); + dto.setLengthCm(BigDecimal.valueOf(50.0)); + + // Simulate petId validation failure + Mockito.doAnswer(invocation -> { + BindingResult result = invocation.getArgument(1); + result.reject("petId", "Validation failed"); + return null; + }).when(petIdExistsValidator).validate(eq(petId), any(BindingResult.class)); + + mockMvc + .perform(post("/pets/{petId}/attributes", petId).contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(dto))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$[0].code").value("petId")); + } + +} diff --git a/src/test/java/org/springframework/samples/petclinic/service/PetAttributesServiceTest.java b/src/test/java/org/springframework/samples/petclinic/service/PetAttributesServiceTest.java new file mode 100644 index 000000000..4350b8d79 --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/service/PetAttributesServiceTest.java @@ -0,0 +1,119 @@ +package org.springframework.samples.petclinic.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.*; +import org.springframework.samples.petclinic.owner.dto.PetAttributesDTO; +import org.springframework.samples.petclinic.owner.expection.PetNotFoundException; +import org.springframework.samples.petclinic.owner.model.Pet; +import org.springframework.samples.petclinic.owner.model.PetAttributes; +import org.springframework.samples.petclinic.owner.repository.PetAttributesRepository; +import org.springframework.samples.petclinic.owner.repository.PetRepository; +import org.springframework.samples.petclinic.owner.service.PetAttributesService; + +import java.math.BigDecimal; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class PetAttributesServiceTest { + + @Mock + private PetAttributesRepository petAttributesRepository; + + @Mock + private PetRepository petRepository; + + @InjectMocks + private PetAttributesService petAttributesService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void testFindByPetId_ReturnsAttributes() { + PetAttributes attributes = new PetAttributes(); + when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.of(attributes)); + Optional result = petAttributesService.findByPetId(1); + assertTrue(result.isPresent()); + assertEquals(attributes, result.get()); + } + + @Test + void testFindByPetId_ReturnsEmpty() { + when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.empty()); + Optional result = petAttributesService.findByPetId(1); + assertFalse(result.isPresent()); + } + + @Test + void testSavePetAttributes_Success_NewRecord() { + Pet pet = new Pet(); + pet.setId(1); + + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setPetId(1); + dto.setTemperament("Calm"); + dto.setLengthCm(BigDecimal.valueOf(40.0)); + dto.setWeightKg(BigDecimal.valueOf(8.5)); + + when(petRepository.findById(1)).thenReturn(Optional.of(pet)); + when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.empty()); + + petAttributesService.savePetAttributes(dto); + + // Expect a new PetAttributes to be created and saved + ArgumentCaptor captor = ArgumentCaptor.forClass(PetAttributes.class); + verify(petAttributesRepository).save(captor.capture()); + + PetAttributes saved = captor.getValue(); + assertEquals("Calm", saved.getTemperament()); + assertEquals(BigDecimal.valueOf(40.0), saved.getLengthCm()); + assertEquals(BigDecimal.valueOf(8.5), saved.getWeightKg()); + assertEquals(pet, saved.getPet()); + } + + @Test + void testSavePetAttributes_Success_UpdateExisting() { + Pet pet = new Pet(); + pet.setId(1); + + PetAttributes existing = new PetAttributes(); + existing.setPet(pet); + + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setPetId(1); + dto.setTemperament("Playful"); + dto.setLengthCm(BigDecimal.valueOf(45.0)); + dto.setWeightKg(BigDecimal.valueOf(9.0)); + + when(petRepository.findById(1)).thenReturn(Optional.of(pet)); + when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.of(existing)); + + petAttributesService.savePetAttributes(dto); + + verify(petAttributesRepository).save(existing); + assertEquals("Playful", existing.getTemperament()); + assertEquals(BigDecimal.valueOf(45.0), existing.getLengthCm()); + assertEquals(BigDecimal.valueOf(9.0), existing.getWeightKg()); + } + + @Test + void testSavePetAttributes_PetNotFound_ThrowsException() { + PetAttributesDTO dto = new PetAttributesDTO(); + dto.setPetId(1); + + when(petRepository.findById(1)).thenReturn(Optional.empty()); + + PetNotFoundException ex = assertThrows(PetNotFoundException.class, () -> { + petAttributesService.savePetAttributes(dto); + }); + + assertEquals("Pet not found", ex.getMessage()); + verify(petAttributesRepository, never()).save(any()); + } + +} 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 005f54863..f3de0c0ba 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java @@ -41,7 +41,8 @@ import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; /** - * Integration Test for {@link org.springframework.samples.petclinic.system.controller.CrashController}. + * Integration Test for + * {@link org.springframework.samples.petclinic.system.controller.CrashController}. * * @author Alex Lutz */ diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java index f60b6819f..7e3f19e08 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java @@ -22,7 +22,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import org.springframework.samples.petclinic.system.controller.CrashController; /** - * Test class for {@link org.springframework.samples.petclinic.system.controller.CrashController} + * Test class for + * {@link org.springframework.samples.petclinic.system.controller.CrashController} * * @author Colin But * @author Alex Lutz