diff --git a/init-workflow.sh b/init-workflow.sh new file mode 100644 index 00000000000..c1d05e71378 --- /dev/null +++ b/init-workflow.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +TASK_ID=${1:-"TASK-001"} +DESC=${2:-"feature-name"} +BRANCH="feature/${TASK_ID}-${DESC}" + +echo "🚀 开始初始化/同步工作流..." + +# 1. 检查是否在仓库内 +if [ ! -d ".git" ]; then + echo "❌ 错误:请在项目根目录执行此脚本" + exit 1 +fi + +# 2. 添加 upstream(若未配置) +if ! git remote | grep -q upstream; then + git remote add upstream https://github.com/spring-projects/spring-petclinic.git + echo "✅ 已添加 upstream 远程仓库" +fi + +# 3. 拉取官方最新元数据 +git fetch upstream + +# 4. 创建/切换到 develop(对齐 main) +if ! git branch --list | grep -q "^develop$"; then + git checkout -b develop upstream/main + echo "✅ 已创建 develop 分支" +else + git checkout develop + git merge --ff-only upstream/main 2>/dev/null || git rebase upstream/main + echo "✅ 已同步 develop 到最新" +fi + +# 5. 清理旧 feature 分支(仅本地,防污染) +if git branch --list | grep -q "^${BRANCH}$"; then + echo "⚠️ 发现旧分支 ${BRANCH},正在删除本地副本..." + git branch -D ${BRANCH} +fi + +# 6. 创建新分支 +git checkout -b ${BRANCH} +echo "✅ 完成!当前分支: ${BRANCH}" +echo "📝 下一步: 打开 IDE/Trae,开始编码。提交前记得 ./mvnw clean test" \ No newline at end of file diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java index 480a7a6900a..9afa5da53e3 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -31,8 +31,9 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OrderBy; import jakarta.persistence.Table; -import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; /** * Simple JavaBean domain object representing an owner. @@ -61,6 +62,11 @@ public class Owner extends Person { @Pattern(regexp = "\\d{10}", message = "{telephone.invalid}") private String telephone; + @Column + @NotBlank + @Email + private String email; + @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "owner_id") @OrderBy("name") @@ -90,6 +96,14 @@ public void setTelephone(String telephone) { this.telephone = telephone; } + public String getEmail() { + return this.email; + } + + public void setEmail(String email) { + this.email = email; + } + public List getPets() { return this.pets; } @@ -153,6 +167,7 @@ public String toString() { .append("address", this.address) .append("city", this.city) .append("telephone", this.telephone) + .append("email", this.email) .toString(); } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index e0689c694f1..31c07b368e9 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -110,11 +110,6 @@ public String processCreationForm(Owner owner, @Valid Pet pet, BindingResult res result.rejectValue("name", "duplicate", "already exists"); } - LocalDate currentDate = LocalDate.now(); - if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) { - result.rejectValue("birthDate", "typeMismatch.birthDate"); - } - if (result.hasErrors()) { return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } @@ -144,11 +139,6 @@ public String processUpdateForm(Owner owner, @Valid Pet pet, BindingResult resul } } - LocalDate currentDate = LocalDate.now(); - if (pet.getBirthDate() != null && pet.getBirthDate().isAfter(currentDate)) { - result.rejectValue("birthDate", "typeMismatch.birthDate"); - } - if (result.hasErrors()) { return VIEWS_PETS_CREATE_OR_UPDATE_FORM; } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java b/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java index dfe5304d57a..e0a82a75e60 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java @@ -15,6 +15,9 @@ */ package org.springframework.samples.petclinic.owner; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + import org.springframework.util.StringUtils; import org.springframework.validation.Errors; import org.springframework.validation.Validator; @@ -51,6 +54,16 @@ public void validate(Object obj, Errors errors) { if (pet.getBirthDate() == null) { errors.rejectValue("birthDate", REQUIRED, REQUIRED); } + else { + LocalDate birthDate = pet.getBirthDate(); + LocalDate now = LocalDate.now(); + if (birthDate.isAfter(now)) { + errors.rejectValue("birthDate", "birthDate.future", "Birth date cannot be in the future"); + } + else if (birthDate.isBefore(now.minusYears(20))) { + errors.rejectValue("birthDate", "birthDate.tooOld", "Pet age cannot exceed 20 years"); + } + } } /** diff --git a/src/main/resources/db/h2/data.sql b/src/main/resources/db/h2/data.sql index f232b1361f4..f25095bdc36 100644 --- a/src/main/resources/db/h2/data.sql +++ b/src/main/resources/db/h2/data.sql @@ -22,16 +22,16 @@ INSERT INTO types VALUES (default, 'snake'); INSERT INTO types VALUES (default, 'bird'); INSERT INTO types VALUES (default, 'hamster'); -INSERT INTO owners VALUES (default, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'); -INSERT INTO owners VALUES (default, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'); -INSERT INTO owners VALUES (default, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763'); -INSERT INTO owners VALUES (default, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198'); -INSERT INTO owners VALUES (default, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765'); -INSERT INTO owners VALUES (default, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654'); -INSERT INTO owners VALUES (default, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387'); -INSERT INTO owners VALUES (default, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683'); -INSERT INTO owners VALUES (default, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435'); -INSERT INTO owners VALUES (default, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487'); +INSERT INTO owners VALUES (default, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023', 'owner1@example.com'); +INSERT INTO owners VALUES (default, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749', 'owner2@example.com'); +INSERT INTO owners VALUES (default, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763', 'owner3@example.com'); +INSERT INTO owners VALUES (default, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198', 'owner4@example.com'); +INSERT INTO owners VALUES (default, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765', 'owner5@example.com'); +INSERT INTO owners VALUES (default, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654', 'owner6@example.com'); +INSERT INTO owners VALUES (default, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387', 'owner7@example.com'); +INSERT INTO owners VALUES (default, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683', 'owner8@example.com'); +INSERT INTO owners VALUES (default, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435', 'owner9@example.com'); +INSERT INTO owners VALUES (default, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487', 'owner10@example.com'); INSERT INTO pets VALUES (default, 'Leo', '2010-09-07', 1, 1); INSERT INTO pets VALUES (default, 'Basil', '2012-08-06', 6, 2); diff --git a/src/main/resources/db/h2/schema.sql b/src/main/resources/db/h2/schema.sql index 4a6c322cbcc..1f1707dd553 100644 --- a/src/main/resources/db/h2/schema.sql +++ b/src/main/resources/db/h2/schema.sql @@ -39,7 +39,8 @@ CREATE TABLE owners ( last_name VARCHAR_IGNORECASE(30), address VARCHAR(255), city VARCHAR(80), - telephone VARCHAR(20) + telephone VARCHAR(20), + email VARCHAR(255) ); CREATE INDEX owners_last_name ON owners (last_name); diff --git a/src/main/resources/db/mysql/data.sql b/src/main/resources/db/mysql/data.sql index 3f1dcf8ea16..d58cc8c0d1a 100644 --- a/src/main/resources/db/mysql/data.sql +++ b/src/main/resources/db/mysql/data.sql @@ -22,16 +22,16 @@ INSERT IGNORE INTO types VALUES (4, 'snake'); INSERT IGNORE INTO types VALUES (5, 'bird'); INSERT IGNORE INTO types VALUES (6, 'hamster'); -INSERT IGNORE INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023'); -INSERT IGNORE INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749'); -INSERT IGNORE INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763'); -INSERT IGNORE INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198'); -INSERT IGNORE INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765'); -INSERT IGNORE INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654'); -INSERT IGNORE INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387'); -INSERT IGNORE INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683'); -INSERT IGNORE INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435'); -INSERT IGNORE INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487'); +INSERT IGNORE INTO owners VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023', 'owner1@example.com'); +INSERT IGNORE INTO owners VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749', 'owner2@example.com'); +INSERT IGNORE INTO owners VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763', 'owner3@example.com'); +INSERT IGNORE INTO owners VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198', 'owner4@example.com'); +INSERT IGNORE INTO owners VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765', 'owner5@example.com'); +INSERT IGNORE INTO owners VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654', 'owner6@example.com'); +INSERT IGNORE INTO owners VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387', 'owner7@example.com'); +INSERT IGNORE INTO owners VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683', 'owner8@example.com'); +INSERT IGNORE INTO owners VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435', 'owner9@example.com'); +INSERT IGNORE INTO owners VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487', 'owner10@example.com'); INSERT IGNORE INTO pets VALUES (1, 'Leo', '2000-09-07', 1, 1); INSERT IGNORE INTO pets VALUES (2, 'Basil', '2002-08-06', 6, 2); diff --git a/src/main/resources/db/postgres/data.sql b/src/main/resources/db/postgres/data.sql index 5b53366ac05..5b2757093d7 100644 --- a/src/main/resources/db/postgres/data.sql +++ b/src/main/resources/db/postgres/data.sql @@ -22,16 +22,16 @@ INSERT INTO types (name) SELECT 'snake' WHERE NOT EXISTS (SELECT * FROM types WH INSERT INTO types (name) SELECT 'bird' WHERE NOT EXISTS (SELECT * FROM types WHERE name='bird'); INSERT INTO types (name) SELECT 'hamster' WHERE NOT EXISTS (SELECT * FROM types WHERE name='hamster'); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=1); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=2); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=3); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=4); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=5); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=6); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=7); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=8); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=9); -INSERT INTO owners (first_name, last_name, address, city, telephone) SELECT 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=10); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023', 'owner1@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=1); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749', 'owner2@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=2); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763', 'owner3@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=3); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198', 'owner4@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=4); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765', 'owner5@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=5); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654', 'owner6@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=6); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387', 'owner7@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=7); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683', 'owner8@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=8); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435', 'owner9@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=9); +INSERT INTO owners (first_name, last_name, address, city, telephone, email) SELECT 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487', 'owner10@example.com' WHERE NOT EXISTS (SELECT * FROM owners WHERE id=10); INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Leo', '2000-09-07', 1, 1 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=1); INSERT INTO pets (name, birth_date, type_id, owner_id) SELECT 'Basil', '2002-08-06', 6, 2 WHERE NOT EXISTS (SELECT * FROM pets WHERE id=2); 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 558b9ae4a92..010f229c336 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -16,23 +16,6 @@ package org.springframework.samples.petclinic.owner; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledInNativeImage; -import org.springframework.beans.factory.annotation.Autowired; -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; -import org.springframework.test.context.aot.DisabledInAotMode; -import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; - -import java.time.LocalDate; -import java.util.List; -import java.util.Optional; - import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasItem; @@ -47,7 +30,28 @@ import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledInNativeImage; +import org.springframework.beans.factory.annotation.Autowired; +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; +import org.springframework.test.context.aot.DisabledInAotMode; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; /** * Test class for {@link OwnerController} @@ -76,6 +80,7 @@ private Owner george() { george.setAddress("110 W. Liberty St."); george.setCity("Madison"); george.setTelephone("6085551023"); + george.setEmail("george@example.com"); Pet max = new Pet(); PetType dog = new PetType(); dog.setName("dog"); @@ -116,18 +121,23 @@ void processCreationFormSuccess() throws Exception { .param("lastName", "Bloggs") .param("address", "123 Caramel Street") .param("city", "London") - .param("telephone", "1316761638")) + .param("telephone", "1316761638") + .param("email", "joe@example.com")) .andExpect(status().is3xxRedirection()); } @Test void processCreationFormHasErrors() throws Exception { mockMvc - .perform(post("/owners/new").param("firstName", "Joe").param("lastName", "Bloggs").param("city", "London")) + .perform(post("/owners/new").param("firstName", "Joe") + .param("lastName", "Bloggs") + .param("city", "London") + .param("email", "invalid-email")) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("owner")) .andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(model().attributeHasFieldErrors("owner", "telephone")) + .andExpect(model().attributeHasFieldErrors("owner", "email")) .andExpect(view().name("owners/createOrUpdateOwnerForm")); } @@ -177,6 +187,7 @@ void initUpdateOwnerForm() throws Exception { .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) + .andExpect(model().attribute("owner", hasProperty("email", is("george@example.com")))) .andExpect(view().name("owners/createOrUpdateOwnerForm")); } @@ -187,7 +198,8 @@ void processUpdateOwnerFormSuccess() throws Exception { .param("lastName", "Bloggs") .param("address", "123 Caramel Street") .param("city", "London") - .param("telephone", "1616291589")) + .param("telephone", "1616291589") + .param("email", "joe@example.com")) .andExpect(status().is3xxRedirection()) .andExpect(view().name("redirect:/owners/{ownerId}")); } @@ -205,11 +217,13 @@ void processUpdateOwnerFormHasErrors() throws Exception { .perform(post("/owners/{ownerId}/edit", TEST_OWNER_ID).param("firstName", "Joe") .param("lastName", "Bloggs") .param("address", "") - .param("telephone", "")) + .param("telephone", "") + .param("email", "joe@")) .andExpect(status().isOk()) .andExpect(model().attributeHasErrors("owner")) .andExpect(model().attributeHasFieldErrors("owner", "address")) .andExpect(model().attributeHasFieldErrors("owner", "telephone")) + .andExpect(model().attributeHasFieldErrors("owner", "email")) .andExpect(view().name("owners/createOrUpdateOwnerForm")); } @@ -222,6 +236,7 @@ void showOwner() throws Exception { .andExpect(model().attribute("owner", hasProperty("address", is("110 W. Liberty St.")))) .andExpect(model().attribute("owner", hasProperty("city", is("Madison")))) .andExpect(model().attribute("owner", hasProperty("telephone", is("6085551023")))) + .andExpect(model().attribute("owner", hasProperty("email", is("george@example.com")))) .andExpect(model().attribute("owner", hasProperty("pets", not(empty())))) .andExpect(model().attribute("owner", hasProperty("pets", hasItem(hasProperty("visits", hasSize(greaterThan(0))))))) @@ -239,6 +254,7 @@ void processUpdateOwnerFormWithIdMismatch() throws Exception { owner.setAddress("Center Street"); owner.setCity("New York"); owner.setTelephone("0123456789"); + owner.setEmail("john@example.com"); when(owners.findById(pathOwnerId)).thenReturn(Optional.of(owner)); 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 e7d16d7c52d..c0b8b1c78c1 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -154,7 +154,7 @@ void processCreationFormWithInvalidBirthDate() throws Exception { .andExpect(model().attributeHasNoErrors("owner")) .andExpect(model().attributeHasErrors("pet")) .andExpect(model().attributeHasFieldErrors("pet", "birthDate")) - .andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "typeMismatch.birthDate")) + .andExpect(model().attributeHasFieldErrorCode("pet", "birthDate", "birthDate.future")) .andExpect(status().isOk()) .andExpect(view().name("pets/createOrUpdatePetForm")); } diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java index d7543ac66ce..098b99bf576 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetValidatorTests.java @@ -52,7 +52,7 @@ class PetValidatorTests { private static final String petTypeName = "Dog"; - private static final LocalDate petBirthDate = LocalDate.of(1990, 1, 1); + private static final LocalDate petBirthDate = LocalDate.of(2020, 1, 1); @BeforeEach void setUp() { @@ -74,6 +74,42 @@ void validate() { assertFalse(errors.hasErrors()); } + @Test + void testValidPetAge_5YearsOld() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(LocalDate.now().minusYears(5)); + + petValidator.validate(pet, errors); + + assertFalse(errors.hasErrors()); + } + + @Test + void testValidPetAge_BornToday() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(LocalDate.now()); + + petValidator.validate(pet, errors); + + assertFalse(errors.hasErrors()); + } + + @Test + void testValidPetAge_Exactly20Years() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(LocalDate.now().minusYears(20)); + + petValidator.validate(pet, errors); + + assertFalse(errors.hasErrors()); + } + @Nested class ValidateHasErrors { @@ -112,6 +148,32 @@ void validateWithInvalidBirthDate() { assertTrue(errors.hasFieldErrors("birthDate")); } + @Test + void testInvalidPetAge_FutureDate() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(LocalDate.now().plusDays(1)); + + petValidator.validate(pet, errors); + + assertTrue(errors.hasFieldErrors("birthDate")); + assertTrue(errors.getFieldError("birthDate").getCode().contains("future")); + } + + @Test + void testInvalidPetAge_Over20Years() { + petType.setName(petTypeName); + pet.setName(petName); + pet.setType(petType); + pet.setBirthDate(LocalDate.now().minusYears(20).minusDays(1)); + + petValidator.validate(pet, errors); + + assertTrue(errors.hasFieldErrors("birthDate")); + assertTrue(errors.getFieldError("birthDate").getCode().contains("tooOld")); + } + } } 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 ddc5bab1ed4..507025d4100 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -116,6 +116,7 @@ void shouldInsertOwner() { owner.setAddress("4, Evans Street"); owner.setCity("Wollongong"); owner.setTelephone("4444444444"); + owner.setEmail("schultz@example.com"); this.owners.save(owner); assertThat(owner.getId()).isNotZero();