From e1b3fa0cf4bf750e3b3792f0e286c388c466b7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B6=A9=EB=A0=AC=28=ED=8A=B8=EB=A0=88=29?= <37261785+takoyakimchi@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:33:43 +0900 Subject: [PATCH] =?UTF-8?q?[BE]=20feat:=20=EB=B0=98=EB=A0=A4=EA=B2=AC=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20API=20(#398)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../friendogly/infra/ImageUpdateType.java | 28 +++ .../pet/controller/PetController.java | 12 ++ .../friendogly/pet/domain/Description.java | 2 +- .../com/happy/friendogly/pet/domain/Name.java | 2 +- .../com/happy/friendogly/pet/domain/Pet.java | 18 +- .../pet/dto/request/SavePetRequest.java | 4 +- .../pet/dto/request/UpdatePetRequest.java | 32 ++++ .../pet/service/PetCommandService.java | 39 ++++ .../happy/friendogly/docs/PetApiDocsTest.java | 98 ++++++++++ .../pet/controller/PetControllerTest.java | 8 +- .../happy/friendogly/pet/domain/PetTest.java | 12 +- .../pet/service/PetCommandServiceTest.java | 167 ++++++++++++++++++ 12 files changed, 407 insertions(+), 15 deletions(-) create mode 100644 backend/src/main/java/com/happy/friendogly/infra/ImageUpdateType.java create mode 100644 backend/src/main/java/com/happy/friendogly/pet/dto/request/UpdatePetRequest.java create mode 100644 backend/src/test/java/com/happy/friendogly/pet/service/PetCommandServiceTest.java diff --git a/backend/src/main/java/com/happy/friendogly/infra/ImageUpdateType.java b/backend/src/main/java/com/happy/friendogly/infra/ImageUpdateType.java new file mode 100644 index 000000000..b96a849f4 --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/infra/ImageUpdateType.java @@ -0,0 +1,28 @@ +package com.happy.friendogly.infra; + +import com.happy.friendogly.exception.FriendoglyException; +import java.util.Arrays; +import java.util.stream.Collectors; + +public enum ImageUpdateType { + + UPDATE, + NOT_UPDATE, + DELETE; + + public static ImageUpdateType from(String rawImageUpdateType) { + try { + return valueOf(rawImageUpdateType); + } catch (IllegalArgumentException e) { + throw new FriendoglyException( + String.format("존재하지 않는 ImageUpdateType 입니다. %s 중 하나로 입력해주세요.", createAllowedValues()) + ); + } + } + + private static String createAllowedValues() { + return Arrays.stream(values()) + .map(Enum::name) + .collect(Collectors.joining(", ")); + } +} diff --git a/backend/src/main/java/com/happy/friendogly/pet/controller/PetController.java b/backend/src/main/java/com/happy/friendogly/pet/controller/PetController.java index f782396f9..b161b81d2 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/controller/PetController.java +++ b/backend/src/main/java/com/happy/friendogly/pet/controller/PetController.java @@ -3,6 +3,7 @@ import com.happy.friendogly.auth.Auth; import com.happy.friendogly.common.ApiResponse; import com.happy.friendogly.pet.dto.request.SavePetRequest; +import com.happy.friendogly.pet.dto.request.UpdatePetRequest; import com.happy.friendogly.pet.dto.response.FindPetResponse; import com.happy.friendogly.pet.dto.response.SavePetResponse; import com.happy.friendogly.pet.service.PetCommandService; @@ -12,6 +13,7 @@ import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -60,4 +62,14 @@ public ApiResponse> findByMemberId(@RequestParam Long memb List response = petQueryService.findByMemberId(memberId); return ApiResponse.ofSuccess(response); } + + @PatchMapping("/{petId}") + public void update( + @PathVariable Long petId, + @Auth Long memberId, + @RequestPart @Valid UpdatePetRequest request, + @RequestPart(required = false) MultipartFile image + ) { + petCommandService.update(memberId, petId, request, image); + } } diff --git a/backend/src/main/java/com/happy/friendogly/pet/domain/Description.java b/backend/src/main/java/com/happy/friendogly/pet/domain/Description.java index 641047c7f..a269ef079 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/domain/Description.java +++ b/backend/src/main/java/com/happy/friendogly/pet/domain/Description.java @@ -14,7 +14,7 @@ public class Description { private static final int MIN_DESCRIPTION_LENGTH = 1; - private static final int MAX_DESCRIPTION_LENGTH = 15; + private static final int MAX_DESCRIPTION_LENGTH = 20; @Column(name = "description", nullable = false) private String value; diff --git a/backend/src/main/java/com/happy/friendogly/pet/domain/Name.java b/backend/src/main/java/com/happy/friendogly/pet/domain/Name.java index 642617711..07f4ed565 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/domain/Name.java +++ b/backend/src/main/java/com/happy/friendogly/pet/domain/Name.java @@ -14,7 +14,7 @@ public class Name { private static final int MIN_NAME_LENGTH = 1; - private static final int MAX_NAME_LENGTH = 15; + private static final int MAX_NAME_LENGTH = 8; @Column(name = "name", nullable = false) private String value; diff --git a/backend/src/main/java/com/happy/friendogly/pet/domain/Pet.java b/backend/src/main/java/com/happy/friendogly/pet/domain/Pet.java index f0c7a14d8..3561e1770 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/domain/Pet.java +++ b/backend/src/main/java/com/happy/friendogly/pet/domain/Pet.java @@ -61,7 +61,7 @@ public Pet( String imageUrl ) { validateMember(member); - + this.member = member; this.name = new Name(name); this.description = new Description(description); @@ -80,4 +80,20 @@ private void validateMember(Member member) { public boolean isOwner(Member member) { return this.member.getId().equals(member.getId()); } + + public void update( + String name, + String description, + LocalDate birthDate, + String sizeType, + String gender, + String imageUrl + ) { + this.name = new Name(name); + this.description = new Description(description); + this.birthDate = new BirthDate(birthDate); + this.sizeType = SizeType.toSizeType(sizeType); + this.gender = Gender.toGender(gender); + this.imageUrl = imageUrl; + } } diff --git a/backend/src/main/java/com/happy/friendogly/pet/dto/request/SavePetRequest.java b/backend/src/main/java/com/happy/friendogly/pet/dto/request/SavePetRequest.java index e75412f1c..b2fde0b73 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/dto/request/SavePetRequest.java +++ b/backend/src/main/java/com/happy/friendogly/pet/dto/request/SavePetRequest.java @@ -10,11 +10,11 @@ public record SavePetRequest( @NotBlank(message = "name은 빈 문자열이나 null을 입력할 수 없습니다.") - @Size(max = 15, message = "이름은 1글자 이상 15글자 이하여야 합니다.") + @Size(max = 8, message = "이름은 1글자 이상 8글자 이하여야 합니다.") String name, @NotBlank(message = "description은 빈 문자열이나 null을 입력할 수 없습니다.") - @Size(max = 15, message = "설명은 1글자 이상 15글자 이하여야 합니다.") + @Size(max = 20, message = "설명은 1글자 이상 20글자 이하여야 합니다.") String description, @NotNull(message = "birthDate는 빈 문자열이나 null을 입력할 수 없습니다.") diff --git a/backend/src/main/java/com/happy/friendogly/pet/dto/request/UpdatePetRequest.java b/backend/src/main/java/com/happy/friendogly/pet/dto/request/UpdatePetRequest.java new file mode 100644 index 000000000..bf3c0ac3a --- /dev/null +++ b/backend/src/main/java/com/happy/friendogly/pet/dto/request/UpdatePetRequest.java @@ -0,0 +1,32 @@ +package com.happy.friendogly.pet.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.PastOrPresent; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; + +public record UpdatePetRequest( + @NotBlank(message = "name은 빈 문자열이나 null을 입력할 수 없습니다.") + @Size(max = 8, message = "이름은 1글자 이상 8글자 이하여야 합니다.") + String name, + + @NotBlank(message = "description은 빈 문자열이나 null을 입력할 수 없습니다.") + @Size(max = 20, message = "설명은 1글자 이상 20글자 이하여야 합니다.") + String description, + + @NotNull(message = "birthDate는 빈 문자열이나 null을 입력할 수 없습니다.") + @PastOrPresent(message = "birthDate는 현재 날짜와 같거나 이전이어야 합니다.") + LocalDate birthDate, + + @NotBlank(message = "sizeType은 빈 문자열이나 null을 입력할 수 없습니다.") + String sizeType, + + @NotBlank(message = "gender는 빈 문자열이나 null을 입력할 수 없습니다.") + String gender, + + @NotBlank(message = "imageUpdateType는 빈 문자열이나 null을 입력할 수 없습니다.") + String imageUpdateType +) { + +} diff --git a/backend/src/main/java/com/happy/friendogly/pet/service/PetCommandService.java b/backend/src/main/java/com/happy/friendogly/pet/service/PetCommandService.java index 1cc36eb0e..8ea99744a 100644 --- a/backend/src/main/java/com/happy/friendogly/pet/service/PetCommandService.java +++ b/backend/src/main/java/com/happy/friendogly/pet/service/PetCommandService.java @@ -2,12 +2,14 @@ import com.happy.friendogly.exception.FriendoglyException; import com.happy.friendogly.infra.FileStorageManager; +import com.happy.friendogly.infra.ImageUpdateType; import com.happy.friendogly.member.domain.Member; import com.happy.friendogly.member.repository.MemberRepository; import com.happy.friendogly.pet.domain.Gender; import com.happy.friendogly.pet.domain.Pet; import com.happy.friendogly.pet.domain.SizeType; import com.happy.friendogly.pet.dto.request.SavePetRequest; +import com.happy.friendogly.pet.dto.request.UpdatePetRequest; import com.happy.friendogly.pet.dto.response.SavePetResponse; import com.happy.friendogly.pet.repository.PetRepository; import org.springframework.stereotype.Service; @@ -64,4 +66,41 @@ private void validatePetCapacity(Long size) { "강아지는 최대 %d 마리까지만 등록할 수 있습니다.", MAX_PET_CAPACITY)); } } + + public void update(Long memberId, Long petId, UpdatePetRequest request, MultipartFile image) { + Member member = memberRepository.getById(memberId); + Pet pet = petRepository.getById(petId); + + if (!pet.isOwner(member)) { + throw new FriendoglyException("자신의 강아지만 수정할 수 있습니다."); + } + + ImageUpdateType imageUpdateType = ImageUpdateType.from(request.imageUpdateType()); + + String oldImageUrl = pet.getImageUrl(); + String newImageUrl = ""; + + if (imageUpdateType == ImageUpdateType.UPDATE) { + // TODO: 기존 이미지 S3에서 삭제 + newImageUrl = fileStorageManager.uploadFile(image); + } + + if (imageUpdateType == ImageUpdateType.NOT_UPDATE) { + newImageUrl = oldImageUrl; + } + + if (imageUpdateType == ImageUpdateType.DELETE) { + // TODO: 기존 이미지 S3에서 삭제 + newImageUrl = ""; + } + + pet.update( + request.name(), + request.description(), + request.birthDate(), + request.sizeType(), + request.gender(), + newImageUrl + ); + } } diff --git a/backend/src/test/java/com/happy/friendogly/docs/PetApiDocsTest.java b/backend/src/test/java/com/happy/friendogly/docs/PetApiDocsTest.java index 48997356d..b286f0743 100644 --- a/backend/src/test/java/com/happy/friendogly/docs/PetApiDocsTest.java +++ b/backend/src/test/java/com/happy/friendogly/docs/PetApiDocsTest.java @@ -17,6 +17,7 @@ import com.happy.friendogly.pet.domain.Gender; import com.happy.friendogly.pet.domain.SizeType; import com.happy.friendogly.pet.dto.request.SavePetRequest; +import com.happy.friendogly.pet.dto.request.UpdatePetRequest; import com.happy.friendogly.pet.dto.response.FindPetResponse; import com.happy.friendogly.pet.dto.response.SavePetResponse; import com.happy.friendogly.pet.service.PetCommandService; @@ -28,10 +29,12 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; public class PetApiDocsTest extends RestDocsTest { @@ -305,6 +308,101 @@ void findByMemberId_Success() throws Exception { ); } + @DisplayName("반려견 정보 업데이트 문서화") + @Test + void update_Success() throws Exception { + // MultipartFile + PATCH Method 같이 사용하기 위한 코드 + MockMultipartHttpServletRequestBuilder patchBuilder + = RestDocumentationRequestBuilders.multipart("/pets/{id}", 1L); + patchBuilder.with(request -> { + request.setMethod(HttpMethod.PATCH.name()); + return request; + }); + + Mockito.doNothing() + .when(petCommandService) + .update(any(), any(), any(), any()); + + UpdatePetRequest requestDto = new UpdatePetRequest( + "도토리", + "도토리 설명", + LocalDate.now().minusYears(1), + "SMALL", + "MALE", + "UPDATE" + ); + + MockMultipartFile image = new MockMultipartFile( + "image", "image.jpg", MediaType.MULTIPART_FORM_DATA.toString(), "asdf".getBytes()); + MockMultipartFile request = new MockMultipartFile( + "request", "request", MediaType.APPLICATION_JSON_VALUE, objectMapper.writeValueAsBytes(requestDto)); + + mockMvc.perform(patchBuilder + .file(image) + .file(request) + .accept(MediaType.APPLICATION_JSON) + .header(HttpHeaders.AUTHORIZATION, getMemberToken())) + .andExpect(status().isOk()) + .andDo(MockMvcRestDocumentationWrapper.document("pet-update-200", + getDocumentRequest(), + getDocumentResponse(), + requestParts( + partWithName("image").description("강아지 프로필 이미지 파일"), + partWithName("request").description("강아지 등록 정보") + ), + requestPartFields( + "request", + fieldWithPath("name").type(JsonFieldType.STRING) + .description("반려견 이름"), + fieldWithPath("description").type(JsonFieldType.STRING) + .description("반려견 한 줄 소개"), + fieldWithPath("birthDate").type(JsonFieldType.STRING) + .description("반려견 생년월일: yyyy-MM-dd"), + fieldWithPath("sizeType").type(JsonFieldType.STRING) + .description("반려견 크기: SMALL, MEDIUM, LARGE"), + fieldWithPath("gender").type(JsonFieldType.STRING) + .description("반려견 성별: MALE, FEMALE, MALE_NEUTERED, FEMALE_NEUTERED"), + fieldWithPath("imageUpdateType").type(JsonFieldType.STRING) + .description("이미지 업데이트 여부: UPDATE, NOT_UPDATE, DELETE") + ), + resource(ResourceSnippetParameters.builder() + .tag("Pet API") + .summary(""" + 반려견 정보 수정 API + + 요청 이미지 + + multipart/form-data "image" (null인 경우 이미지 변경을 하지 않음) + + 요청 데이터 + + application/json "request" + + { + + "name": "보리", // 변경할 강아지 이름 + + "description": "귀여운 보리입니다", // 변경할 강아지 설명 + + "birthDate": "2011-01-01", // 강아지 생일 yyyy-MM-dd + + "sizeType": "SMALL", // 변경할 강아지 크기 (SMALL, MEDIUM, LARGE) + + "gender": "MALE", // 변경할 반려견 성별 (MALE, FEMALE, MALE_NEUTERED, FEMALE_NEUTERED) + + "imageUpdateType": "UPDATE" // 이미지 업데이트 여부 -> UPDATE(이미지 변경), NOT_UPDATE(이미지 변경 없음), DELETE(기본 이미지로 변경) + + } + """) + .requestHeaders( + headerWithName(HttpHeaders.AUTHORIZATION).description("로그인한 회원의 accessToken") + ) + .pathParameters( + parameterWithName("id").description("수정하려는 Pet ID")) + .build())) + ); + } + @Override protected Object controller() { return new PetController(petQueryService, petCommandService); diff --git a/backend/src/test/java/com/happy/friendogly/pet/controller/PetControllerTest.java b/backend/src/test/java/com/happy/friendogly/pet/controller/PetControllerTest.java index 638e82a7e..c0d2b6221 100644 --- a/backend/src/test/java/com/happy/friendogly/pet/controller/PetControllerTest.java +++ b/backend/src/test/java/com/happy/friendogly/pet/controller/PetControllerTest.java @@ -60,11 +60,11 @@ void savePet() { .statusCode(HttpStatus.CREATED.value()); } - @DisplayName("닉네임 길이가 15자를 초과하는 경우 400을 반환한다.") + @DisplayName("닉네임 길이가 8자를 초과하는 경우 400을 반환한다.") @Test void savePet_Fail_NameLengthOver() { SavePetRequest request = new SavePetRequest( - "1234567890123456", + "123456789", "땡이입니다.", LocalDate.now().minusDays(1L), "SMALL", @@ -82,12 +82,12 @@ void savePet_Fail_NameLengthOver() { .statusCode(HttpStatus.BAD_REQUEST.value()); } - @DisplayName("한 줄 설명의 길이가 15자를 초과하는 경우 400을 반환한다.") + @DisplayName("한 줄 설명의 길이가 20자를 초과하는 경우 400을 반환한다.") @Test void savePet_Fail_DescriptionLengthOver() { SavePetRequest request = new SavePetRequest( "땡이", - "1234567890123456", + "123456789012345678901", LocalDate.now().minusDays(1L), "SMALL", "FEMALE_NEUTERED", diff --git a/backend/src/test/java/com/happy/friendogly/pet/domain/PetTest.java b/backend/src/test/java/com/happy/friendogly/pet/domain/PetTest.java index 7c0b5b9f7..d3adf5f1b 100644 --- a/backend/src/test/java/com/happy/friendogly/pet/domain/PetTest.java +++ b/backend/src/test/java/com/happy/friendogly/pet/domain/PetTest.java @@ -65,13 +65,13 @@ void create_Fail_NullName() { .hasMessage("이름은 빈 값이나 null일 수 없습니다."); } - @DisplayName("이름의 길이가 16글자 이상인 경우 예외가 발생한다.") + @DisplayName("이름의 길이가 8글자를 초과하는 경우 예외가 발생한다.") @Test void create_Fail_IllegalNameLength() { assertThatThrownBy(() -> Pet.builder() .member(member) - .name("1234567890123456") + .name("123456789") .description("땡이입니다.") .birthDate(LocalDate.now().minusDays(1L)) .sizeType(SizeType.SMALL) @@ -79,7 +79,7 @@ void create_Fail_IllegalNameLength() { .imageUrl("http://www.google.com") .build()) .isExactlyInstanceOf(FriendoglyException.class) - .hasMessage("이름은 1자 이상, 15자 이하여야 합니다."); + .hasMessage("이름은 1자 이상, 8자 이하여야 합니다."); } @DisplayName("한 줄 설명이 null인 경우 예외가 발생한다.") @@ -99,21 +99,21 @@ void create_Fail_NullDescription() { .hasMessage("한 줄 설명은 빈 값이나 null일 수 없습니다."); } - @DisplayName("한 줄 설명의 길이가 16글자 이상인 경우 예외가 발생한다.") + @DisplayName("한 줄 설명의 길이가 20글자를 초과하는 경우 예외가 발생한다.") @Test void create_Fail_IllegalDescriptionLength() { assertThatThrownBy(() -> Pet.builder() .member(member) .name("땡이") - .description("1234567890123456") + .description("123456789012345678901") .birthDate(LocalDate.now().minusDays(1L)) .sizeType(SizeType.SMALL) .gender(Gender.FEMALE_NEUTERED) .imageUrl("http://www.google.com") .build()) .isExactlyInstanceOf(FriendoglyException.class) - .hasMessage("한 줄 설명은 1자 이상, 15자 이하여야 합니다."); + .hasMessage("한 줄 설명은 1자 이상, 20자 이하여야 합니다."); } @DisplayName("생년월일이 현재 날짜보다 미래인 경우 예외가 발생한다.") diff --git a/backend/src/test/java/com/happy/friendogly/pet/service/PetCommandServiceTest.java b/backend/src/test/java/com/happy/friendogly/pet/service/PetCommandServiceTest.java new file mode 100644 index 000000000..265dfd35b --- /dev/null +++ b/backend/src/test/java/com/happy/friendogly/pet/service/PetCommandServiceTest.java @@ -0,0 +1,167 @@ +package com.happy.friendogly.pet.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.happy.friendogly.exception.FriendoglyException; +import com.happy.friendogly.member.domain.Member; +import com.happy.friendogly.pet.domain.Gender; +import com.happy.friendogly.pet.domain.Pet; +import com.happy.friendogly.pet.domain.SizeType; +import com.happy.friendogly.pet.dto.request.UpdatePetRequest; +import com.happy.friendogly.support.ServiceTest; +import java.time.LocalDate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockMultipartFile; + +class PetCommandServiceTest extends ServiceTest { + + @Autowired + private PetCommandService petCommandService; + + private Member member; + + @BeforeEach + void setUp() { + member = memberRepository.save(new Member("트레", "1b32cff0", "https://image.com/image.jpg")); + } + + @DisplayName("펫 정보를 수정할 수 있다 (이미지 변경)") + @Test + void update() { + // given + Pet pet = petRepository.save(new Pet( + member, + "도토리", + "귀여운 도토리입니다!", + LocalDate.now().minusYears(1), + SizeType.SMALL, + Gender.MALE_NEUTERED, + "https://picsum.photos/100" + )); + + UpdatePetRequest request = new UpdatePetRequest( + "바둑이", + "새로운 펫이에요!", + LocalDate.now().minusYears(2), + "MEDIUM", + "MALE", + "UPDATE" + ); + + MockMultipartFile image = new MockMultipartFile("image", new byte[10]); + + // when + petCommandService.update(member.getId(), pet.getId(), request, image); + + // then + Pet newPet = petRepository.getById(pet.getId()); + assertAll( + () -> assertThat(newPet.getId()).isEqualTo(pet.getId()), + () -> assertThat(newPet.getMember().getId()).isEqualTo(pet.getMember().getId()), + () -> assertThat(newPet.getName().getValue()).isEqualTo("바둑이"), + () -> assertThat(newPet.getDescription().getValue()).isEqualTo("새로운 펫이에요!"), + () -> assertThat(newPet.getBirthDate().getValue()).isEqualTo(LocalDate.now().minusYears(2)), + () -> assertThat(newPet.getSizeType()).isEqualTo(SizeType.MEDIUM), + () -> assertThat(newPet.getGender()).isEqualTo(Gender.MALE), + () -> assertThat(newPet.getImageUrl()).startsWith("http://localhost/") + ); + } + + @DisplayName("펫 정보를 수정할 수 있다 (이미지 변경 없음)") + @Test + void update_ImageNotUpdate() { + // given + Pet pet = petRepository.save(new Pet( + member, + "도토리", + "귀여운 도토리입니다!", + LocalDate.now().minusYears(1), + SizeType.SMALL, + Gender.MALE_NEUTERED, + "https://picsum.photos/100" + )); + + UpdatePetRequest request = new UpdatePetRequest( + "바둑이", + "새로운 펫이에요!", + LocalDate.now().minusYears(2), + "MEDIUM", + "MALE", + "NOT_UPDATE" + ); + + // when + petCommandService.update(member.getId(), pet.getId(), request, null); + + // then + Pet newPet = petRepository.getById(pet.getId()); + assertThat(newPet.getImageUrl()).isEqualTo("https://picsum.photos/100"); + } + + @DisplayName("펫 정보를 수정할 수 있다 (default 이미지로 변경)") + @Test + void update_ImageDelete() { + // given + Pet pet = petRepository.save(new Pet( + member, + "도토리", + "귀여운 도토리입니다!", + LocalDate.now().minusYears(1), + SizeType.SMALL, + Gender.MALE_NEUTERED, + "https://picsum.photos/100" + )); + + UpdatePetRequest request = new UpdatePetRequest( + "바둑이", + "새로운 펫이에요!", + LocalDate.now().minusYears(2), + "MEDIUM", + "MALE", + "DELETE" + ); + + // when + petCommandService.update(member.getId(), pet.getId(), request, null); + + // then + Pet newPet = petRepository.getById(pet.getId()); + assertThat(newPet.getImageUrl()).isBlank(); + } + + @DisplayName("자신의 펫이 아니면 펫 정보를 수정할 수 없다.") + @Test + void update_NotMyPet() { + // given + Member other = memberRepository.save(new Member("다른사람", "12cdd5fb", "https://image.com/image.jpg")); + + Pet pet = petRepository.save(new Pet( + other, + "도토리", + "귀여운 도토리입니다!", + LocalDate.now().minusYears(1), + SizeType.SMALL, + Gender.MALE_NEUTERED, + "https://picsum.photos/100" + )); + + UpdatePetRequest request = new UpdatePetRequest( + "바둑이", + "새로운 펫이에요!", + LocalDate.now().minusYears(2), + "MEDIUM", + "MALE", + "UPDATE" + ); + + // when - then + assertThatThrownBy(() -> petCommandService.update(member.getId(), pet.getId(), request, null)) + .isInstanceOf(FriendoglyException.class) + .hasMessage("자신의 강아지만 수정할 수 있습니다."); + } +}