mirror of
https://github.com/spring-projects/spring-petclinic.git
synced 2025-07-19 22:15:50 +00:00
Save pet attributes and retrieve them feature completed.
This commit is contained in:
parent
e76ed81718
commit
8c8509b46e
17 changed files with 621 additions and 5 deletions
BIN
src.zip
BIN
src.zip
Binary file not shown.
|
@ -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<PetAttributes> optionalAttributes = petAttributesService.findByPetId(petId);
|
||||||
|
|
||||||
|
return optionalAttributes.<ResponseEntity<?>>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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.springframework.samples.petclinic.owner.expection;
|
||||||
|
|
||||||
|
public class PetNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
public PetNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <code>PetAttributes</code> 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<PetAttributes, Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find pet attributes by pet ID.
|
||||||
|
*
|
||||||
|
* @param petId the ID of the pet
|
||||||
|
* @return an Optional containing the PetAttributes if found
|
||||||
|
*/
|
||||||
|
Optional<PetAttributes> findByPetId(Integer petId);
|
||||||
|
|
||||||
|
}
|
|
@ -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<Pet, Integer> {
|
||||||
|
|
||||||
|
}
|
|
@ -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<PetAttributes> findByPetId(Integer petId) {
|
||||||
|
return petAttributesRepository.findByPetId(petId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void savePetAttributes(PetAttributesDTO dto) {
|
||||||
|
Optional<Pet> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
database=mysql
|
database=mysql
|
||||||
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
|
spring.datasource.url=${MYSQL_URL:jdbc:mysql://localhost/petclinic}
|
||||||
spring.datasource.username=root
|
spring.datasource.username=root
|
||||||
spring.datasource.password=testA@123
|
spring.datasource.password=your-password
|
||||||
# SQL is written to be idempotent so this is safe
|
# SQL is written to be idempotent so this is safe
|
||||||
spring.sql.init.mode=always
|
spring.sql.init.mode=always
|
||||||
|
|
||||||
|
|
|
@ -53,3 +53,13 @@ CREATE TABLE IF NOT EXISTS visits (
|
||||||
description VARCHAR(255),
|
description VARCHAR(255),
|
||||||
FOREIGN KEY (pet_id) REFERENCES pets(id)
|
FOREIGN KEY (pet_id) REFERENCES pets(id)
|
||||||
) engine=InnoDB;
|
) 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)
|
||||||
|
);
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<PetAttributes> result = petAttributesService.findByPetId(1);
|
||||||
|
assertTrue(result.isPresent());
|
||||||
|
assertEquals(attributes, result.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFindByPetId_ReturnsEmpty() {
|
||||||
|
when(petAttributesRepository.findByPetId(1)).thenReturn(Optional.empty());
|
||||||
|
Optional<PetAttributes> 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<PetAttributes> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -41,7 +41,8 @@ import org.springframework.http.RequestEntity;
|
||||||
import org.springframework.http.ResponseEntity;
|
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
|
* @author Alex Lutz
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -22,7 +22,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import org.springframework.samples.petclinic.system.controller.CrashController;
|
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 Colin But
|
||||||
* @author Alex Lutz
|
* @author Alex Lutz
|
||||||
|
|
Loading…
Reference in a new issue