From c8f3349b714d74526bce246d44a9dbd902b21710 Mon Sep 17 00:00:00 2001 From: songhyeonpk Date: Thu, 22 May 2025 14:34:18 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=A8=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A7=88=EB=AC=B8=20=EC=83=9D=EC=84=B1,=20=EC=88=98=EC=A0=95,?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeleteGroomingTestQuestionController.java | 26 +++++ .../SaveGroomingTestQuestionController.java | 31 ++++++ .../UpdateGroomingTestQuestionController.java | 31 ++++++ .../SaveGroomingTestQuestionRequest.java | 15 +++ .../UpdateGroomingTestQuestionRequest.java | 13 +++ .../out/cache/CaffeineCacheAdapter.java | 9 +- .../GroomingDomainPersistenceAdapter.java | 44 +++++++- .../model/GroomingTestQuestionJpaEntity.java | 6 + .../GroomingTestAnswerRepository.java | 10 +- .../DeleteGroomingTestQuestionCommand.java | 17 +++ .../SaveGroomingTestQuestionCommand.java | 21 ++++ .../UpdateGroomingTestQuestionCommand.java | 24 ++++ .../DeleteGroomingTestQuestionUseCase.java | 10 ++ .../SaveGroomingTestQuestionUseCase.java | 10 ++ .../UpdateGroomingTestQuestionUseCase.java | 10 ++ .../InvalidGroomingTestsWithCachePort.java | 9 ++ .../DeleteGroomingTestAnswerPort.java | 9 ++ .../DeleteGroomingTestQuestionPort.java | 9 ++ .../LoadGroomingTestQuestionPort.java | 4 + .../SaveGroomingTestQuestionPort.java | 10 ++ .../UpdateGroomingTestQuestionPort.java | 10 ++ .../DeleteGroomingTestQuestionService.java | 32 ++++++ .../SaveGroomingTestQuestionService.java | 30 +++++ .../UpdateGroomingTestQuestionService.java | 43 ++++++++ .../response/enums/ErrorResponseCode.java | 2 + .../domain/entity/GroomingTestQuestion.java | 15 +++ .../com/ftm/server/domain/entity/User.java | 9 ++ .../security/SecurityConfig.java | 5 + src/test/java/com/ftm/server/BaseTest.java | 23 ++++ .../DeleteGroomingTestQuestionTest.java | 70 ++++++++++++ .../SaveGroomingTestQuestionTest.java | 60 ++++++++++ .../UpdateGroomingTestQuestionTest.java | 104 ++++++++++++++++++ 32 files changed, 718 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/DeleteGroomingTestQuestionController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/SaveGroomingTestQuestionController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/UpdateGroomingTestQuestionController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestQuestionRequest.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestQuestionRequest.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/DeleteGroomingTestQuestionCommand.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/SaveGroomingTestQuestionCommand.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/UpdateGroomingTestQuestionCommand.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/question/DeleteGroomingTestQuestionUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/question/SaveGroomingTestQuestionUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/question/UpdateGroomingTestQuestionUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/out/cache/InvalidGroomingTestsWithCachePort.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestQuestionPort.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestQuestionPort.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestQuestionPort.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/question/DeleteGroomingTestQuestionService.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/question/SaveGroomingTestQuestionService.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/question/UpdateGroomingTestQuestionService.java create mode 100644 src/test/java/com/ftm/server/grooming/question/DeleteGroomingTestQuestionTest.java create mode 100644 src/test/java/com/ftm/server/grooming/question/SaveGroomingTestQuestionTest.java create mode 100644 src/test/java/com/ftm/server/grooming/question/UpdateGroomingTestQuestionTest.java diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/DeleteGroomingTestQuestionController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/DeleteGroomingTestQuestionController.java new file mode 100644 index 0000000..81fbdb2 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/DeleteGroomingTestQuestionController.java @@ -0,0 +1,26 @@ +package com.ftm.server.adapter.in.web.grooming.controller.question; + +import com.ftm.server.application.command.grooming.DeleteGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.DeleteGroomingTestQuestionUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class DeleteGroomingTestQuestionController { + + private final DeleteGroomingTestQuestionUseCase deleteGroomingTestQuestionUseCase; + + @DeleteMapping("/api/grooming/tests/questions/{questionId}") + public ResponseEntity> deleteQuestion(@PathVariable Long questionId) { + deleteGroomingTestQuestionUseCase.execute(DeleteGroomingTestQuestionCommand.of(questionId)); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessResponseCode.OK)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/SaveGroomingTestQuestionController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/SaveGroomingTestQuestionController.java new file mode 100644 index 0000000..596d552 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/SaveGroomingTestQuestionController.java @@ -0,0 +1,31 @@ +package com.ftm.server.adapter.in.web.grooming.controller.question; + +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestQuestionRequest; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.SaveGroomingTestQuestionUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class SaveGroomingTestQuestionController { + + private final SaveGroomingTestQuestionUseCase saveGroomingTestQuestionUseCase; + + @PostMapping("/api/grooming/tests/questions") + public ResponseEntity> saveQuestion( + @RequestBody @Valid SaveGroomingTestQuestionRequest request) { + saveGroomingTestQuestionUseCase.execute( + SaveGroomingTestQuestionCommand.of( + request.getGroomingCategory(), request.getQuestion())); + return ResponseEntity.status(HttpStatus.CREATED) + .body(ApiResponse.success(SuccessResponseCode.CREATED)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/UpdateGroomingTestQuestionController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/UpdateGroomingTestQuestionController.java new file mode 100644 index 0000000..9e73559 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/question/UpdateGroomingTestQuestionController.java @@ -0,0 +1,31 @@ +package com.ftm.server.adapter.in.web.grooming.controller.question; + +import com.ftm.server.adapter.in.web.grooming.dto.request.UpdateGroomingTestQuestionRequest; +import com.ftm.server.application.command.grooming.UpdateGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.UpdateGroomingTestQuestionUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class UpdateGroomingTestQuestionController { + + private final UpdateGroomingTestQuestionUseCase updateGroomingTestQuestionUseCase; + + @PatchMapping("/api/grooming/tests/questions/{questionId}") + public ResponseEntity> updateQuestion( + @PathVariable Long questionId, @RequestBody UpdateGroomingTestQuestionRequest request) { + updateGroomingTestQuestionUseCase.execute( + UpdateGroomingTestQuestionCommand.of( + questionId, request.getGroomingCategory(), request.getQuestion())); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessResponseCode.OK)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestQuestionRequest.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestQuestionRequest.java new file mode 100644 index 0000000..9d2c54a --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestQuestionRequest.java @@ -0,0 +1,15 @@ +package com.ftm.server.adapter.in.web.grooming.dto.request; + +import com.ftm.server.domain.enums.GroomingCategory; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SaveGroomingTestQuestionRequest { + + @NotNull private GroomingCategory groomingCategory; + @NotEmpty private String question; +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestQuestionRequest.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestQuestionRequest.java new file mode 100644 index 0000000..e1422fa --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestQuestionRequest.java @@ -0,0 +1,13 @@ +package com.ftm.server.adapter.in.web.grooming.dto.request; + +import com.ftm.server.domain.enums.GroomingCategory; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UpdateGroomingTestQuestionRequest { + + private GroomingCategory groomingCategory; + private String question; +} diff --git a/src/main/java/com/ftm/server/adapter/out/cache/CaffeineCacheAdapter.java b/src/main/java/com/ftm/server/adapter/out/cache/CaffeineCacheAdapter.java index 48b9498..2af0cb4 100644 --- a/src/main/java/com/ftm/server/adapter/out/cache/CaffeineCacheAdapter.java +++ b/src/main/java/com/ftm/server/adapter/out/cache/CaffeineCacheAdapter.java @@ -7,6 +7,7 @@ import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; import com.ftm.server.adapter.out.persistence.repository.GroomingTestAnswerRepository; import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; import com.ftm.server.application.port.out.cache.LoadGroomingTestsWithCachePort; import com.ftm.server.application.vo.grooming.GroomingTestAnswerInfoVo; import com.ftm.server.application.vo.grooming.GroomingTestQuestionWithAnswersVo; @@ -19,12 +20,14 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @Slf4j @Adapter @RequiredArgsConstructor -public class CaffeineCacheAdapter implements LoadGroomingTestsWithCachePort { +public class CaffeineCacheAdapter + implements LoadGroomingTestsWithCachePort, InvalidGroomingTestsWithCachePort { private final GroomingTestQuestionRepository groomingTestQuestionRepository; private final GroomingTestAnswerRepository groomingTestAnswerRepository; @@ -68,4 +71,8 @@ public List loadGroomingTestsCache() { question.getId(), List.of()))) .collect(Collectors.toCollection(ArrayList::new)); } + + @CacheEvict(value = GROOMING_TESTS_INFO_CACHE_NAME, key = GROOMING_TESTS_INFO_CACHE_KEY_ALL) + @Override + public void invalidGroomingTestsCache() {} } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java index 9e258cd..cb4892e 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java @@ -29,7 +29,11 @@ public class GroomingDomainPersistenceAdapter SaveGroomingTestResultPort, LoadGroomingLevelPort, UpdateUserForGroomingPort, - LoadGroomingTestResultPort { + LoadGroomingTestResultPort, + SaveGroomingTestQuestionPort, + UpdateGroomingTestQuestionPort, + DeleteGroomingTestQuestionPort, + DeleteGroomingTestAnswerPort { // Repository private final GroomingTestQuestionRepository groomingTestQuestionRepository; @@ -53,6 +57,13 @@ public List loadGroomingTestQuestions() { .collect(Collectors.toList()); } + @Override + public Optional loadGroomingTestQuestionById(FindByIdQuery query) { + return groomingTestQuestionRepository + .findById(query.getId()) + .map(groomingTestQuestionMapper::toDomain); + } + @Override public List loadGroomingTestAnswers() { List answers = groomingTestAnswerRepository.findAll(); @@ -136,4 +147,35 @@ public List loadByUserIdAndTestedAt( return results.stream().map(groomingTestResultMapper::toDomainEntity).toList(); } + + @Override + public void saveGroomingTestQuestion(GroomingTestQuestion groomingTestQuestion) { + groomingTestQuestionRepository.save( + groomingTestQuestionMapper.toJpaEntity(groomingTestQuestion)); + } + + @Override + public void updateGroomingTestQuestion(GroomingTestQuestion groomingTestQuestion) { + GroomingTestQuestionJpaEntity groomingTestQuestionJpaEntity = + groomingTestQuestionRepository + .findById(groomingTestQuestion.getId()) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode + .GROOMING_TEST_QUESTION_NOT_FOUND)); + + groomingTestQuestionJpaEntity.updateGroomingTestQuestionForDomainEntity( + groomingTestQuestion); + } + + @Override + public void deleteGroomingTestQuestionById(Long id) { + groomingTestQuestionRepository.deleteById(id); + } + + @Override + public void deleteGroomingTestAnswersByQuestionId(Long questionId) { + groomingTestAnswerRepository.deleteAllByGroomingTestQuestionId(questionId); + } } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestQuestionJpaEntity.java b/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestQuestionJpaEntity.java index 02b3d8a..17df512 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestQuestionJpaEntity.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestQuestionJpaEntity.java @@ -39,4 +39,10 @@ public static GroomingTestQuestionJpaEntity from(GroomingTestQuestion groomingTe .question(groomingTestQuestion.getQuestion()) .build(); } + + public void updateGroomingTestQuestionForDomainEntity( + GroomingTestQuestion groomingTestQuestion) { + this.groomingCategory = groomingTestQuestion.getGroomingCategory(); + this.question = groomingTestQuestion.getQuestion(); + } } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestAnswerRepository.java b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestAnswerRepository.java index d74ff76..4d6dc99 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestAnswerRepository.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestAnswerRepository.java @@ -2,6 +2,14 @@ import com.ftm.server.adapter.out.persistence.model.GroomingTestAnswerJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; public interface GroomingTestAnswerRepository - extends JpaRepository {} + extends JpaRepository { + + @Modifying + @Query( + "DELETE FROM GroomingTestAnswerJpaEntity gta WHERE gta.groomingTestQuestion.id = (:questionId)") + void deleteAllByGroomingTestQuestionId(Long questionId); +} diff --git a/src/main/java/com/ftm/server/application/command/grooming/DeleteGroomingTestQuestionCommand.java b/src/main/java/com/ftm/server/application/command/grooming/DeleteGroomingTestQuestionCommand.java new file mode 100644 index 0000000..5c12c6e --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/DeleteGroomingTestQuestionCommand.java @@ -0,0 +1,17 @@ +package com.ftm.server.application.command.grooming; + +import lombok.Getter; + +@Getter +public class DeleteGroomingTestQuestionCommand { + + private final Long id; + + private DeleteGroomingTestQuestionCommand(Long id) { + this.id = id; + } + + public static DeleteGroomingTestQuestionCommand of(Long id) { + return new DeleteGroomingTestQuestionCommand(id); + } +} diff --git a/src/main/java/com/ftm/server/application/command/grooming/SaveGroomingTestQuestionCommand.java b/src/main/java/com/ftm/server/application/command/grooming/SaveGroomingTestQuestionCommand.java new file mode 100644 index 0000000..3399030 --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/SaveGroomingTestQuestionCommand.java @@ -0,0 +1,21 @@ +package com.ftm.server.application.command.grooming; + +import com.ftm.server.domain.enums.GroomingCategory; +import lombok.Getter; + +@Getter +public class SaveGroomingTestQuestionCommand { + + private final GroomingCategory groomingCategory; + private final String question; + + private SaveGroomingTestQuestionCommand(GroomingCategory groomingCategory, String question) { + this.groomingCategory = groomingCategory; + this.question = question; + } + + public static SaveGroomingTestQuestionCommand of( + GroomingCategory groomingCategory, String question) { + return new SaveGroomingTestQuestionCommand(groomingCategory, question); + } +} diff --git a/src/main/java/com/ftm/server/application/command/grooming/UpdateGroomingTestQuestionCommand.java b/src/main/java/com/ftm/server/application/command/grooming/UpdateGroomingTestQuestionCommand.java new file mode 100644 index 0000000..6e037ac --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/UpdateGroomingTestQuestionCommand.java @@ -0,0 +1,24 @@ +package com.ftm.server.application.command.grooming; + +import com.ftm.server.domain.enums.GroomingCategory; +import lombok.Getter; + +@Getter +public class UpdateGroomingTestQuestionCommand { + + private final Long id; + private final GroomingCategory groomingCategory; + private final String question; + + private UpdateGroomingTestQuestionCommand( + Long id, GroomingCategory groomingCategory, String question) { + this.id = id; + this.groomingCategory = groomingCategory; + this.question = question; + } + + public static UpdateGroomingTestQuestionCommand of( + Long id, GroomingCategory groomingCategory, String question) { + return new UpdateGroomingTestQuestionCommand(id, groomingCategory, question); + } +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/question/DeleteGroomingTestQuestionUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/question/DeleteGroomingTestQuestionUseCase.java new file mode 100644 index 0000000..f0e9044 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/question/DeleteGroomingTestQuestionUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.question; + +import com.ftm.server.application.command.grooming.DeleteGroomingTestQuestionCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface DeleteGroomingTestQuestionUseCase { + + void execute(DeleteGroomingTestQuestionCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/question/SaveGroomingTestQuestionUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/question/SaveGroomingTestQuestionUseCase.java new file mode 100644 index 0000000..f3a03d9 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/question/SaveGroomingTestQuestionUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.question; + +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface SaveGroomingTestQuestionUseCase { + + void execute(SaveGroomingTestQuestionCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/question/UpdateGroomingTestQuestionUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/question/UpdateGroomingTestQuestionUseCase.java new file mode 100644 index 0000000..ed89b11 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/question/UpdateGroomingTestQuestionUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.question; + +import com.ftm.server.application.command.grooming.UpdateGroomingTestQuestionCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface UpdateGroomingTestQuestionUseCase { + + void execute(UpdateGroomingTestQuestionCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/out/cache/InvalidGroomingTestsWithCachePort.java b/src/main/java/com/ftm/server/application/port/out/cache/InvalidGroomingTestsWithCachePort.java new file mode 100644 index 0000000..03951b7 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/cache/InvalidGroomingTestsWithCachePort.java @@ -0,0 +1,9 @@ +package com.ftm.server.application.port.out.cache; + +import com.ftm.server.common.annotation.Port; + +@Port +public interface InvalidGroomingTestsWithCachePort { + + void invalidGroomingTestsCache(); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java new file mode 100644 index 0000000..687810f --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java @@ -0,0 +1,9 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; + +@Port +public interface DeleteGroomingTestAnswerPort { + + void deleteGroomingTestAnswersByQuestionId(Long questionId); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestQuestionPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestQuestionPort.java new file mode 100644 index 0000000..313fbcc --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestQuestionPort.java @@ -0,0 +1,9 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; + +@Port +public interface DeleteGroomingTestQuestionPort { + + void deleteGroomingTestQuestionById(Long id); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestQuestionPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestQuestionPort.java index 4673beb..53ef508 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestQuestionPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestQuestionPort.java @@ -1,11 +1,15 @@ package com.ftm.server.application.port.out.persistence.grooming; +import com.ftm.server.application.query.FindByIdQuery; import com.ftm.server.common.annotation.Port; import com.ftm.server.domain.entity.GroomingTestQuestion; import java.util.List; +import java.util.Optional; @Port public interface LoadGroomingTestQuestionPort { List loadGroomingTestQuestions(); + + Optional loadGroomingTestQuestionById(FindByIdQuery query); } diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestQuestionPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestQuestionPort.java new file mode 100644 index 0000000..f31ea59 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestQuestionPort.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; +import com.ftm.server.domain.entity.GroomingTestQuestion; + +@Port +public interface SaveGroomingTestQuestionPort { + + void saveGroomingTestQuestion(GroomingTestQuestion groomingTestQuestion); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestQuestionPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestQuestionPort.java new file mode 100644 index 0000000..f76f8a3 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestQuestionPort.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; +import com.ftm.server.domain.entity.GroomingTestQuestion; + +@Port +public interface UpdateGroomingTestQuestionPort { + + void updateGroomingTestQuestion(GroomingTestQuestion groomingTestQuestion); +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/question/DeleteGroomingTestQuestionService.java b/src/main/java/com/ftm/server/application/service/grooming/question/DeleteGroomingTestQuestionService.java new file mode 100644 index 0000000..ff91e56 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/question/DeleteGroomingTestQuestionService.java @@ -0,0 +1,32 @@ +package com.ftm.server.application.service.grooming.question; + +import com.ftm.server.application.command.grooming.DeleteGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.DeleteGroomingTestQuestionUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.DeleteGroomingTestAnswerPort; +import com.ftm.server.application.port.out.persistence.grooming.DeleteGroomingTestQuestionPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class DeleteGroomingTestQuestionService implements DeleteGroomingTestQuestionUseCase { + + private final DeleteGroomingTestAnswerPort deleteGroomingTestAnswerPort; + private final DeleteGroomingTestQuestionPort deleteGroomingTestQuestionPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(DeleteGroomingTestQuestionCommand command) { + // 질문의 답변 목록 삭제 + deleteGroomingTestAnswerPort.deleteGroomingTestAnswersByQuestionId(command.getId()); + + // 질문 삭제 + deleteGroomingTestQuestionPort.deleteGroomingTestQuestionById(command.getId()); + + // 그루밍 테스트 캐싱 목록 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/question/SaveGroomingTestQuestionService.java b/src/main/java/com/ftm/server/application/service/grooming/question/SaveGroomingTestQuestionService.java new file mode 100644 index 0000000..0758540 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/question/SaveGroomingTestQuestionService.java @@ -0,0 +1,30 @@ +package com.ftm.server.application.service.grooming.question; + +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.SaveGroomingTestQuestionUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.SaveGroomingTestQuestionPort; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class SaveGroomingTestQuestionService implements SaveGroomingTestQuestionUseCase { + + private final SaveGroomingTestQuestionPort saveGroomingTestQuestionPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(SaveGroomingTestQuestionCommand command) { + GroomingTestQuestion groomingTestQuestion = GroomingTestQuestion.create(command); + + // 그루밍 테스트 질문 저장 + saveGroomingTestQuestionPort.saveGroomingTestQuestion(groomingTestQuestion); + + // 그루밍 테스트 캐싱 정보 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/question/UpdateGroomingTestQuestionService.java b/src/main/java/com/ftm/server/application/service/grooming/question/UpdateGroomingTestQuestionService.java new file mode 100644 index 0000000..767ddc4 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/question/UpdateGroomingTestQuestionService.java @@ -0,0 +1,43 @@ +package com.ftm.server.application.service.grooming.question; + +import com.ftm.server.application.command.grooming.UpdateGroomingTestQuestionCommand; +import com.ftm.server.application.port.in.grooming.question.UpdateGroomingTestQuestionUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.LoadGroomingTestQuestionPort; +import com.ftm.server.application.port.out.persistence.grooming.UpdateGroomingTestQuestionPort; +import com.ftm.server.application.query.FindByIdQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UpdateGroomingTestQuestionService implements UpdateGroomingTestQuestionUseCase { + + private final LoadGroomingTestQuestionPort loadGroomingTestQuestionPort; + private final UpdateGroomingTestQuestionPort updateGroomingTestQuestionPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(UpdateGroomingTestQuestionCommand command) { + GroomingTestQuestion question = + loadGroomingTestQuestionPort + .loadGroomingTestQuestionById(FindByIdQuery.of(command.getId())) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode + .GROOMING_TEST_QUESTION_NOT_FOUND)); + + // 그루밍 테스트 질문 업데이트 + question.update(command); + updateGroomingTestQuestionPort.updateGroomingTestQuestion(question); + + // 그루밍 테스트 캐시 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java index fbd29c8..24b00fc 100644 --- a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java +++ b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java @@ -52,6 +52,8 @@ public enum ErrorResponseCode { POST_PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "E404_006", "요청한 상품을 찾을 수 없습니다."), POST_PRODUCT_IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "E404_007", "요청한 상품 이미지를 찾을 수 없습니다."), BOOKMARK_NOT_FOUND(HttpStatus.NOT_FOUND, "E404_008", "요청된 사용자와 게시글에 부합하는 북마크를 찾을 수 없습니다."), + GROOMING_TEST_QUESTION_NOT_FOUND( + HttpStatus.NOT_FOUND, "E404_009", "요청한 그루밍 테스트 질문 정보를 찾을 수 없습니다."), // 409번 USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "E409_001", "이미 존재하는 사용자입니다."), diff --git a/src/main/java/com/ftm/server/domain/entity/GroomingTestQuestion.java b/src/main/java/com/ftm/server/domain/entity/GroomingTestQuestion.java index 545dc44..eaab04d 100644 --- a/src/main/java/com/ftm/server/domain/entity/GroomingTestQuestion.java +++ b/src/main/java/com/ftm/server/domain/entity/GroomingTestQuestion.java @@ -1,5 +1,7 @@ package com.ftm.server.domain.entity; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.application.command.grooming.UpdateGroomingTestQuestionCommand; import com.ftm.server.domain.enums.GroomingCategory; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -43,4 +45,17 @@ public static GroomingTestQuestion of( .updatedAt(updatedAt) .build(); } + + public static GroomingTestQuestion create(SaveGroomingTestQuestionCommand command) { + return GroomingTestQuestion.builder() + .groomingCategory(command.getGroomingCategory()) + .question(command.getQuestion()) + .build(); + } + + public void update(UpdateGroomingTestQuestionCommand command) { + if (command.getGroomingCategory() != null) + this.groomingCategory = command.getGroomingCategory(); + if (command.getQuestion() != null) this.question = command.getQuestion(); + } } diff --git a/src/main/java/com/ftm/server/domain/entity/User.java b/src/main/java/com/ftm/server/domain/entity/User.java index 5b46a00..ccb9482 100644 --- a/src/main/java/com/ftm/server/domain/entity/User.java +++ b/src/main/java/com/ftm/server/domain/entity/User.java @@ -149,6 +149,15 @@ public static User createTestKakaoUser() { .build(); } + public static User createAdminUser(String email, String password, String nickname) { + return User.builder() + .email(email) + .password(password) + .nickname(nickname) + .role(UserRole.ADMIN) + .build(); + } + public void updateGroomingInfo(Integer groomingScore, Long groomingLevelId) { this.groomingScore = groomingScore; this.groomingLevelId = groomingLevelId; diff --git a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java index 8e6a081..b4bc0e4 100644 --- a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java +++ b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java @@ -1,6 +1,7 @@ package com.ftm.server.infrastructure.security; import com.ftm.server.adapter.out.security.UserPrincipalAdapter; +import com.ftm.server.domain.enums.UserRole; import com.ftm.server.infrastructure.security.handler.PermissionDeniedHandler; import com.ftm.server.infrastructure.security.handler.UnauthenticatedAccessHandler; import java.util.List; @@ -116,6 +117,10 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(HttpMethod.POST, POST_ANONYMOUS_MATCHERS) .permitAll(); + authorize + .requestMatchers("/api/grooming/tests/questions/**") + .hasRole(UserRole.ADMIN.getValue()); + // 그 외 모든 요청은 인증 필요 authorize.anyRequest().authenticated(); }); diff --git a/src/test/java/com/ftm/server/BaseTest.java b/src/test/java/com/ftm/server/BaseTest.java index a4c483f..6e4bfc4 100644 --- a/src/test/java/com/ftm/server/BaseTest.java +++ b/src/test/java/com/ftm/server/BaseTest.java @@ -170,4 +170,27 @@ protected SessionAndUser createUserAndLoginAndReturnUser(String email, String pa return new SessionAndUser(session, user); } + + protected MockHttpSession createAdminUserAndLogin() { + String email = "admin@gmail.com"; + String password = "admin1234!"; + String nickname = "admintest"; + + User admin = User.createAdminUser(email, password, nickname); + + // session 생성 + SecurityContext context = SecurityContextHolder.createEmptyContext(); + UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken( + UserPrincipal.of(admin), + null, + List.of(new SimpleGrantedAuthority("ROLE_" + UserRole.ADMIN.name()))); + context.setAuthentication(auth); + + MockHttpSession session = new MockHttpSession(); + session.setAttribute( + HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context); + + return session; + } } diff --git a/src/test/java/com/ftm/server/grooming/question/DeleteGroomingTestQuestionTest.java b/src/test/java/com/ftm/server/grooming/question/DeleteGroomingTestQuestionTest.java new file mode 100644 index 0000000..dd1e19b --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/question/DeleteGroomingTestQuestionTest.java @@ -0,0 +1,70 @@ +package com.ftm.server.grooming.question; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class DeleteGroomingTestQuestionTest extends BaseTest { + + @Autowired private GroomingTestQuestionRepository groomingTestQuestionRepository; + @Autowired private GroomingTestQuestionMapper groomingTestQuestionMapper; + + private Long questionId; + + private ResultActions getResultActions(Long questionId, MockHttpSession session) + throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.delete( + "/api/grooming/tests/questions/{questionId}", questionId) + .session(session)); + } + + @BeforeEach + void setUp() { + SaveGroomingTestQuestionCommand command = + SaveGroomingTestQuestionCommand.of(GroomingCategory.BEAUTY, "그루밍 테스트 질문"); + GroomingTestQuestion question = GroomingTestQuestion.create(command); + questionId = + groomingTestQuestionRepository + .save(groomingTestQuestionMapper.toJpaEntity(question)) + .getId(); + } + + @Test + @Transactional + void 그루밍_테스트_질문_삭제_성공() throws Exception { + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(questionId, session); + + // given + resultActions.andExpect(status().isOk()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_질문_삭제_실패() throws Exception { + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(questionId, session); + + // given + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } +} diff --git a/src/test/java/com/ftm/server/grooming/question/SaveGroomingTestQuestionTest.java b/src/test/java/com/ftm/server/grooming/question/SaveGroomingTestQuestionTest.java new file mode 100644 index 0000000..92b5dbb --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/question/SaveGroomingTestQuestionTest.java @@ -0,0 +1,60 @@ +package com.ftm.server.grooming.question; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.payload.JsonFieldType.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestQuestionRequest; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class SaveGroomingTestQuestionTest extends BaseTest { + + private ResultActions getResultActions( + SaveGroomingTestQuestionRequest request, MockHttpSession session) throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/grooming/tests/questions") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(request)) + .session(session)); + } + + @Test + @Transactional + void 그루밍_테스트_질문_저장_성공() throws Exception { + // given + SaveGroomingTestQuestionRequest request = + new SaveGroomingTestQuestionRequest(GroomingCategory.BEAUTY, "테스트 질문입니다."); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(request, session); + + // given + resultActions.andExpect(status().isCreated()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_질문_저장_실패() throws Exception { + // given + SaveGroomingTestQuestionRequest request = + new SaveGroomingTestQuestionRequest(GroomingCategory.BEAUTY, "테스트 질문입니다."); + + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(request, session); + + // given + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } +} diff --git a/src/test/java/com/ftm/server/grooming/question/UpdateGroomingTestQuestionTest.java b/src/test/java/com/ftm/server/grooming/question/UpdateGroomingTestQuestionTest.java new file mode 100644 index 0000000..2c5158a --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/question/UpdateGroomingTestQuestionTest.java @@ -0,0 +1,104 @@ +package com.ftm.server.grooming.question; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.grooming.dto.request.UpdateGroomingTestQuestionRequest; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class UpdateGroomingTestQuestionTest extends BaseTest { + + @Autowired private GroomingTestQuestionRepository groomingTestQuestionRepository; + @Autowired private GroomingTestQuestionMapper groomingTestQuestionMapper; + + private Long questionId; + + private ResultActions getResultActions( + Long questionId, UpdateGroomingTestQuestionRequest request, MockHttpSession session) + throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.patch( + "/api/grooming/tests/questions/{questionId}", questionId) + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(request)) + .session(session)); + } + + @BeforeEach + void setUp() { + SaveGroomingTestQuestionCommand command = + SaveGroomingTestQuestionCommand.of(GroomingCategory.BEAUTY, "그루밍 테스트 질문"); + GroomingTestQuestion question = GroomingTestQuestion.create(command); + questionId = + groomingTestQuestionRepository + .save(groomingTestQuestionMapper.toJpaEntity(question)) + .getId(); + } + + @Test + @Transactional + void 그루밍_테스트_질문_수정_성공() throws Exception { + // given + UpdateGroomingTestQuestionRequest request = + new UpdateGroomingTestQuestionRequest(null, "질문 수정"); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(questionId, request, session); + + // given + resultActions.andExpect(status().isOk()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_질문_수정_실패1() throws Exception { + // given + UpdateGroomingTestQuestionRequest request = + new UpdateGroomingTestQuestionRequest(null, "질문 수정"); + + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(questionId, request, session); + + // given + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_질문_수정_실패2() throws Exception { + // given + UpdateGroomingTestQuestionRequest request = + new UpdateGroomingTestQuestionRequest(null, "질문 수정"); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(1000000L, request, session); + + // given + resultActions + .andExpect( + status().is( + ErrorResponseCode.GROOMING_TEST_QUESTION_NOT_FOUND + .getHttpStatus() + .value())) + .andDo(print()); + } +} From 289345bf2c1cc0d538658265f5ac3d186d13878a Mon Sep 17 00:00:00 2001 From: songhyeonpk Date: Fri, 23 May 2025 15:08:31 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=A8=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=8B=B5=EB=B3=80=20=EC=83=9D=EC=84=B1,=20=EC=88=98=EC=A0=95,?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?(#125)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DeleteGroomingTestAnswerController.java | 26 ++++ .../SaveGroomingTestAnswerController.java | 33 +++++ .../UpdateGroomingTestAnswerController.java | 34 +++++ .../SaveGroomingTestAnswerRequest.java | 16 ++ .../UpdateGroomingTestAnswerRequest.java | 13 ++ .../GroomingDomainPersistenceAdapter.java | 63 +++++++- .../model/GroomingTestAnswerJpaEntity.java | 13 ++ .../DeleteGroomingTestAnswerCommand.java | 17 +++ .../answer/SaveGroomingTestAnswerCommand.java | 21 +++ .../UpdateGroomingTestAnswerCommand.java | 25 ++++ .../DeleteGroomingTestAnswerUseCase.java | 10 ++ .../answer/SaveGroomingTestAnswerUseCase.java | 10 ++ .../UpdateGroomingTestAnswerUseCase.java | 10 ++ .../DeleteGroomingTestAnswerPort.java | 2 + .../grooming/LoadGroomingTestAnswerPort.java | 4 + .../grooming/SaveGroomingTestAnswerPort.java | 10 ++ .../UpdateGroomingTestAnswerPort.java | 11 ++ .../DeleteGroomingTestAnswerService.java | 27 ++++ .../answer/SaveGroomingTestAnswerService.java | 28 ++++ .../UpdateGroomingTestAnswerService.java | 45 ++++++ .../response/enums/ErrorResponseCode.java | 2 + .../domain/entity/GroomingTestAnswer.java | 16 ++ .../security/SecurityConfig.java | 4 +- .../answer/DeleteGroomingTestAnswerTest.java | 84 +++++++++++ .../answer/SaveGroomingTestAnswerTest.java | 101 +++++++++++++ .../answer/UpdateGroomingTestAnswerTest.java | 139 ++++++++++++++++++ 26 files changed, 762 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/DeleteGroomingTestAnswerController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/SaveGroomingTestAnswerController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/UpdateGroomingTestAnswerController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestAnswerRequest.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestAnswerRequest.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/answer/DeleteGroomingTestAnswerCommand.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/answer/SaveGroomingTestAnswerCommand.java create mode 100644 src/main/java/com/ftm/server/application/command/grooming/answer/UpdateGroomingTestAnswerCommand.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/answer/DeleteGroomingTestAnswerUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/answer/SaveGroomingTestAnswerUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/answer/UpdateGroomingTestAnswerUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestAnswerPort.java create mode 100644 src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestAnswerPort.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/answer/DeleteGroomingTestAnswerService.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/answer/SaveGroomingTestAnswerService.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/answer/UpdateGroomingTestAnswerService.java create mode 100644 src/test/java/com/ftm/server/grooming/answer/DeleteGroomingTestAnswerTest.java create mode 100644 src/test/java/com/ftm/server/grooming/answer/SaveGroomingTestAnswerTest.java create mode 100644 src/test/java/com/ftm/server/grooming/answer/UpdateGroomingTestAnswerTest.java diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/DeleteGroomingTestAnswerController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/DeleteGroomingTestAnswerController.java new file mode 100644 index 0000000..b95c16d --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/DeleteGroomingTestAnswerController.java @@ -0,0 +1,26 @@ +package com.ftm.server.adapter.in.web.grooming.controller.answer; + +import com.ftm.server.application.command.grooming.answer.DeleteGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.DeleteGroomingTestAnswerUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class DeleteGroomingTestAnswerController { + + private final DeleteGroomingTestAnswerUseCase deleteGroomingTestAnswerUseCase; + + @DeleteMapping("/api/grooming/tests/answers/{answerId}") + public ResponseEntity> deleteAnswer(@PathVariable Long answerId) { + deleteGroomingTestAnswerUseCase.execute(DeleteGroomingTestAnswerCommand.of(answerId)); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessResponseCode.OK)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/SaveGroomingTestAnswerController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/SaveGroomingTestAnswerController.java new file mode 100644 index 0000000..34580f3 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/SaveGroomingTestAnswerController.java @@ -0,0 +1,33 @@ +package com.ftm.server.adapter.in.web.grooming.controller.answer; + +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestAnswerRequest; +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.SaveGroomingTestAnswerUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class SaveGroomingTestAnswerController { + + private final SaveGroomingTestAnswerUseCase saveGroomingTestAnswerUseCase; + + @PostMapping("/api/grooming/tests/questions/{questionsId}/answers") + public ResponseEntity> saveAnswer( + @PathVariable Long questionsId, + @RequestBody @Valid SaveGroomingTestAnswerRequest request) { + saveGroomingTestAnswerUseCase.execute( + SaveGroomingTestAnswerCommand.of( + questionsId, request.getAnswer(), request.getScore())); + return ResponseEntity.status(HttpStatus.CREATED) + .body(ApiResponse.success(SuccessResponseCode.CREATED)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/UpdateGroomingTestAnswerController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/UpdateGroomingTestAnswerController.java new file mode 100644 index 0000000..1e631c7 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/answer/UpdateGroomingTestAnswerController.java @@ -0,0 +1,34 @@ +package com.ftm.server.adapter.in.web.grooming.controller.answer; + +import com.ftm.server.adapter.in.web.grooming.dto.request.UpdateGroomingTestAnswerRequest; +import com.ftm.server.application.command.grooming.answer.UpdateGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.UpdateGroomingTestAnswerUseCase; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class UpdateGroomingTestAnswerController { + + private final UpdateGroomingTestAnswerUseCase updateGroomingTestAnswerUseCase; + + @PatchMapping("/api/grooming/tests/answers/{answerId}") + public ResponseEntity> updateAnswer( + @PathVariable Long answerId, @RequestBody UpdateGroomingTestAnswerRequest request) { + updateGroomingTestAnswerUseCase.execute( + UpdateGroomingTestAnswerCommand.of( + answerId, + request.getQuestionId(), + request.getAnswer(), + request.getScore())); + return ResponseEntity.status(HttpStatus.OK) + .body(ApiResponse.success(SuccessResponseCode.OK)); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestAnswerRequest.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestAnswerRequest.java new file mode 100644 index 0000000..7d156a5 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/SaveGroomingTestAnswerRequest.java @@ -0,0 +1,16 @@ +package com.ftm.server.adapter.in.web.grooming.dto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SaveGroomingTestAnswerRequest { + + @NotEmpty private String answer; + + @Min(0) + private Integer score; +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestAnswerRequest.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestAnswerRequest.java new file mode 100644 index 0000000..7f02a0c --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/request/UpdateGroomingTestAnswerRequest.java @@ -0,0 +1,13 @@ +package com.ftm.server.adapter.in.web.grooming.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UpdateGroomingTestAnswerRequest { + + private Long questionId; + private String answer; + private Integer score; +} diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java index cb4892e..8524ddc 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java @@ -33,7 +33,9 @@ public class GroomingDomainPersistenceAdapter SaveGroomingTestQuestionPort, UpdateGroomingTestQuestionPort, DeleteGroomingTestQuestionPort, - DeleteGroomingTestAnswerPort { + DeleteGroomingTestAnswerPort, + SaveGroomingTestAnswerPort, + UpdateGroomingTestAnswerPort { // Repository private final GroomingTestQuestionRepository groomingTestQuestionRepository; @@ -72,6 +74,13 @@ public List loadGroomingTestAnswers() { .collect(Collectors.toList()); } + @Override + public Optional loadGroomingTestAnswerById(FindByIdQuery query) { + return groomingTestAnswerRepository + .findById(query.getId()) + .map(groomingTestAnswerMapper::toDomain); + } + @Override public Optional loadUser(FindByIdQuery query) { return userRepository.findById(query.getId()).map(userMapper::toDomainEntity); @@ -178,4 +187,56 @@ public void deleteGroomingTestQuestionById(Long id) { public void deleteGroomingTestAnswersByQuestionId(Long questionId) { groomingTestAnswerRepository.deleteAllByGroomingTestQuestionId(questionId); } + + @Override + public void deleteGroomingTestAnswerById(Long id) { + groomingTestAnswerRepository.deleteById(id); + } + + @Override + public void saveGroomingTestAnswer(GroomingTestAnswer groomingTestAnswer) { + GroomingTestQuestionJpaEntity groomingTestQuestionJpaEntity = + groomingTestQuestionRepository + .findById(groomingTestAnswer.getGroomingTestQuestionId()) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode + .GROOMING_TEST_QUESTION_NOT_FOUND)); + + groomingTestAnswerRepository.save( + groomingTestAnswerMapper.toJpaEntity( + groomingTestAnswer, groomingTestQuestionJpaEntity)); + } + + @Override + public void updateGroomingTestAnswer( + GroomingTestAnswer groomingTestAnswer, boolean isQuestionModified) { + GroomingTestAnswerJpaEntity groomingTestAnswerJpaEntity = + groomingTestAnswerRepository + .findById(groomingTestAnswer.getId()) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode.GROOMING_TEST_ANSWER_NOT_FOUND)); + + // 답변이 속한 질문을 수정했을 경우, 질문 정보까지 함께 수정 + if (isQuestionModified) { + GroomingTestQuestionJpaEntity groomingTestQuestionJpaEntity = + groomingTestQuestionRepository + .findById(groomingTestAnswer.getGroomingTestQuestionId()) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode + .GROOMING_TEST_QUESTION_NOT_FOUND)); + + groomingTestAnswerJpaEntity.updateGroomingTestAnswerForDomainEntity( + groomingTestQuestionJpaEntity, groomingTestAnswer); + return; + } + + // 질문 정보를 제외한 나머지 필드 수정 + groomingTestAnswerJpaEntity.updateGroomingTestAnswerForDomainEntity(groomingTestAnswer); + } } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestAnswerJpaEntity.java b/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestAnswerJpaEntity.java index 6cef31e..ba3a5cb 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestAnswerJpaEntity.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/model/GroomingTestAnswerJpaEntity.java @@ -43,4 +43,17 @@ public static GroomingTestAnswerJpaEntity from( .score(groomingTestAnswer.getScore()) .build(); } + + public void updateGroomingTestAnswerForDomainEntity( + GroomingTestQuestionJpaEntity groomingTestQuestionJpaEntity, + GroomingTestAnswer groomingTestAnswer) { + this.groomingTestQuestion = groomingTestQuestionJpaEntity; + this.answer = groomingTestAnswer.getAnswer(); + this.score = groomingTestAnswer.getScore(); + } + + public void updateGroomingTestAnswerForDomainEntity(GroomingTestAnswer groomingTestAnswer) { + this.answer = groomingTestAnswer.getAnswer(); + this.score = groomingTestAnswer.getScore(); + } } diff --git a/src/main/java/com/ftm/server/application/command/grooming/answer/DeleteGroomingTestAnswerCommand.java b/src/main/java/com/ftm/server/application/command/grooming/answer/DeleteGroomingTestAnswerCommand.java new file mode 100644 index 0000000..d024fed --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/answer/DeleteGroomingTestAnswerCommand.java @@ -0,0 +1,17 @@ +package com.ftm.server.application.command.grooming.answer; + +import lombok.Getter; + +@Getter +public class DeleteGroomingTestAnswerCommand { + + private final Long id; + + private DeleteGroomingTestAnswerCommand(Long id) { + this.id = id; + } + + public static DeleteGroomingTestAnswerCommand of(Long id) { + return new DeleteGroomingTestAnswerCommand(id); + } +} diff --git a/src/main/java/com/ftm/server/application/command/grooming/answer/SaveGroomingTestAnswerCommand.java b/src/main/java/com/ftm/server/application/command/grooming/answer/SaveGroomingTestAnswerCommand.java new file mode 100644 index 0000000..ad1a38b --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/answer/SaveGroomingTestAnswerCommand.java @@ -0,0 +1,21 @@ +package com.ftm.server.application.command.grooming.answer; + +import lombok.Getter; + +@Getter +public class SaveGroomingTestAnswerCommand { + + private final Long questionId; + private final String answer; + private final Integer score; + + private SaveGroomingTestAnswerCommand(Long questionId, String answer, Integer score) { + this.questionId = questionId; + this.answer = answer; + this.score = score; + } + + public static SaveGroomingTestAnswerCommand of(Long questionId, String answer, Integer score) { + return new SaveGroomingTestAnswerCommand(questionId, answer, score); + } +} diff --git a/src/main/java/com/ftm/server/application/command/grooming/answer/UpdateGroomingTestAnswerCommand.java b/src/main/java/com/ftm/server/application/command/grooming/answer/UpdateGroomingTestAnswerCommand.java new file mode 100644 index 0000000..b41af3a --- /dev/null +++ b/src/main/java/com/ftm/server/application/command/grooming/answer/UpdateGroomingTestAnswerCommand.java @@ -0,0 +1,25 @@ +package com.ftm.server.application.command.grooming.answer; + +import lombok.Getter; + +@Getter +public class UpdateGroomingTestAnswerCommand { + + private final Long id; + private final Long questionId; + private final String answer; + private final Integer score; + + private UpdateGroomingTestAnswerCommand( + Long id, Long questionId, String answer, Integer score) { + this.id = id; + this.questionId = questionId; + this.answer = answer; + this.score = score; + } + + public static UpdateGroomingTestAnswerCommand of( + Long id, Long questionId, String answer, Integer score) { + return new UpdateGroomingTestAnswerCommand(id, questionId, answer, score); + } +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/answer/DeleteGroomingTestAnswerUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/answer/DeleteGroomingTestAnswerUseCase.java new file mode 100644 index 0000000..15da9ee --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/answer/DeleteGroomingTestAnswerUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.DeleteGroomingTestAnswerCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface DeleteGroomingTestAnswerUseCase { + + void execute(DeleteGroomingTestAnswerCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/answer/SaveGroomingTestAnswerUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/answer/SaveGroomingTestAnswerUseCase.java new file mode 100644 index 0000000..f2ce53a --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/answer/SaveGroomingTestAnswerUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface SaveGroomingTestAnswerUseCase { + + void execute(SaveGroomingTestAnswerCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/answer/UpdateGroomingTestAnswerUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/answer/UpdateGroomingTestAnswerUseCase.java new file mode 100644 index 0000000..58b3afb --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/answer/UpdateGroomingTestAnswerUseCase.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.in.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.UpdateGroomingTestAnswerCommand; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface UpdateGroomingTestAnswerUseCase { + + void execute(UpdateGroomingTestAnswerCommand command); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java index 687810f..2dc4715 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/DeleteGroomingTestAnswerPort.java @@ -6,4 +6,6 @@ public interface DeleteGroomingTestAnswerPort { void deleteGroomingTestAnswersByQuestionId(Long questionId); + + void deleteGroomingTestAnswerById(Long id); } diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestAnswerPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestAnswerPort.java index 6e044c9..c7b4045 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestAnswerPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestAnswerPort.java @@ -1,11 +1,15 @@ package com.ftm.server.application.port.out.persistence.grooming; +import com.ftm.server.application.query.FindByIdQuery; import com.ftm.server.common.annotation.Port; import com.ftm.server.domain.entity.GroomingTestAnswer; import java.util.List; +import java.util.Optional; @Port public interface LoadGroomingTestAnswerPort { List loadGroomingTestAnswers(); + + Optional loadGroomingTestAnswerById(FindByIdQuery query); } diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestAnswerPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestAnswerPort.java new file mode 100644 index 0000000..9c456e4 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/SaveGroomingTestAnswerPort.java @@ -0,0 +1,10 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; +import com.ftm.server.domain.entity.GroomingTestAnswer; + +@Port +public interface SaveGroomingTestAnswerPort { + + void saveGroomingTestAnswer(GroomingTestAnswer groomingTestAnswer); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestAnswerPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestAnswerPort.java new file mode 100644 index 0000000..f326d21 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/UpdateGroomingTestAnswerPort.java @@ -0,0 +1,11 @@ +package com.ftm.server.application.port.out.persistence.grooming; + +import com.ftm.server.common.annotation.Port; +import com.ftm.server.domain.entity.GroomingTestAnswer; + +@Port +public interface UpdateGroomingTestAnswerPort { + + void updateGroomingTestAnswer( + GroomingTestAnswer groomingTestAnswer, boolean isQuestionModified); +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/answer/DeleteGroomingTestAnswerService.java b/src/main/java/com/ftm/server/application/service/grooming/answer/DeleteGroomingTestAnswerService.java new file mode 100644 index 0000000..5ef2021 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/answer/DeleteGroomingTestAnswerService.java @@ -0,0 +1,27 @@ +package com.ftm.server.application.service.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.DeleteGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.DeleteGroomingTestAnswerUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.DeleteGroomingTestAnswerPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class DeleteGroomingTestAnswerService implements DeleteGroomingTestAnswerUseCase { + + private final DeleteGroomingTestAnswerPort deleteGroomingTestAnswerPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(DeleteGroomingTestAnswerCommand command) { + // 답변 삭제 + deleteGroomingTestAnswerPort.deleteGroomingTestAnswerById(command.getId()); + + // 그루밍 테스트 캐싱 목록 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/answer/SaveGroomingTestAnswerService.java b/src/main/java/com/ftm/server/application/service/grooming/answer/SaveGroomingTestAnswerService.java new file mode 100644 index 0000000..be22710 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/answer/SaveGroomingTestAnswerService.java @@ -0,0 +1,28 @@ +package com.ftm.server.application.service.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.SaveGroomingTestAnswerUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.SaveGroomingTestAnswerPort; +import com.ftm.server.domain.entity.GroomingTestAnswer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class SaveGroomingTestAnswerService implements SaveGroomingTestAnswerUseCase { + + private final SaveGroomingTestAnswerPort saveGroomingTestAnswerPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(SaveGroomingTestAnswerCommand command) { + GroomingTestAnswer newAnswer = GroomingTestAnswer.create(command); + saveGroomingTestAnswerPort.saveGroomingTestAnswer(newAnswer); + + // 그루밍 테스트 캐싱 목록 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/answer/UpdateGroomingTestAnswerService.java b/src/main/java/com/ftm/server/application/service/grooming/answer/UpdateGroomingTestAnswerService.java new file mode 100644 index 0000000..3b8dd01 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/answer/UpdateGroomingTestAnswerService.java @@ -0,0 +1,45 @@ +package com.ftm.server.application.service.grooming.answer; + +import com.ftm.server.application.command.grooming.answer.UpdateGroomingTestAnswerCommand; +import com.ftm.server.application.port.in.grooming.answer.UpdateGroomingTestAnswerUseCase; +import com.ftm.server.application.port.out.cache.InvalidGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.LoadGroomingTestAnswerPort; +import com.ftm.server.application.port.out.persistence.grooming.UpdateGroomingTestAnswerPort; +import com.ftm.server.application.query.FindByIdQuery; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestAnswer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class UpdateGroomingTestAnswerService implements UpdateGroomingTestAnswerUseCase { + + private final LoadGroomingTestAnswerPort loadGroomingTestAnswerPort; + private final UpdateGroomingTestAnswerPort updateGroomingTestAnswerPort; + private final InvalidGroomingTestsWithCachePort invalidGroomingTestsWithCachePort; + + @Transactional + @Override + public void execute(UpdateGroomingTestAnswerCommand command) { + GroomingTestAnswer answer = + loadGroomingTestAnswerPort + .loadGroomingTestAnswerById(FindByIdQuery.of(command.getId())) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode.GROOMING_TEST_ANSWER_NOT_FOUND)); + + // 답변이 속한 질문 변경 여부 + boolean isQuestionModified = command.getQuestionId() != null; + + // 답변 업데이트 + answer.update(command); + updateGroomingTestAnswerPort.updateGroomingTestAnswer(answer, isQuestionModified); + + // 그루밍 테스트 캐싱 목록 초기화 + invalidGroomingTestsWithCachePort.invalidGroomingTestsCache(); + } +} diff --git a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java index 24b00fc..8d61eed 100644 --- a/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java +++ b/src/main/java/com/ftm/server/common/response/enums/ErrorResponseCode.java @@ -54,6 +54,8 @@ public enum ErrorResponseCode { BOOKMARK_NOT_FOUND(HttpStatus.NOT_FOUND, "E404_008", "요청된 사용자와 게시글에 부합하는 북마크를 찾을 수 없습니다."), GROOMING_TEST_QUESTION_NOT_FOUND( HttpStatus.NOT_FOUND, "E404_009", "요청한 그루밍 테스트 질문 정보를 찾을 수 없습니다."), + GROOMING_TEST_ANSWER_NOT_FOUND( + HttpStatus.NOT_FOUND, "E404_010", "요청한 그루밍 테스트 답변 정보를 찾을 수 없습니다."), // 409번 USER_ALREADY_EXISTS(HttpStatus.CONFLICT, "E409_001", "이미 존재하는 사용자입니다."), diff --git a/src/main/java/com/ftm/server/domain/entity/GroomingTestAnswer.java b/src/main/java/com/ftm/server/domain/entity/GroomingTestAnswer.java index 8bb1e19..f8b96c2 100644 --- a/src/main/java/com/ftm/server/domain/entity/GroomingTestAnswer.java +++ b/src/main/java/com/ftm/server/domain/entity/GroomingTestAnswer.java @@ -1,5 +1,7 @@ package com.ftm.server.domain.entity; +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.application.command.grooming.answer.UpdateGroomingTestAnswerCommand; import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.Builder; @@ -47,4 +49,18 @@ public static GroomingTestAnswer of( .updatedAt(updatedAt) .build(); } + + public static GroomingTestAnswer create(SaveGroomingTestAnswerCommand command) { + return GroomingTestAnswer.builder() + .groomingTestQuestionId(command.getQuestionId()) + .answer(command.getAnswer()) + .score(command.getScore()) + .build(); + } + + public void update(UpdateGroomingTestAnswerCommand command) { + if (command.getQuestionId() != null) this.groomingTestQuestionId = command.getQuestionId(); + if (command.getAnswer() != null) this.answer = command.getAnswer(); + if (command.getScore() != null) this.score = command.getScore(); + } } diff --git a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java index b4bc0e4..f05a791 100644 --- a/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java +++ b/src/main/java/com/ftm/server/infrastructure/security/SecurityConfig.java @@ -119,7 +119,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti authorize .requestMatchers("/api/grooming/tests/questions/**") - .hasRole(UserRole.ADMIN.getValue()); + .hasRole(UserRole.ADMIN.name()) + .requestMatchers("/api/grooming/tests/answers/**") + .hasRole(UserRole.ADMIN.name()); // 그 외 모든 요청은 인증 필요 authorize.anyRequest().authenticated(); diff --git a/src/test/java/com/ftm/server/grooming/answer/DeleteGroomingTestAnswerTest.java b/src/test/java/com/ftm/server/grooming/answer/DeleteGroomingTestAnswerTest.java new file mode 100644 index 0000000..3550a3d --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/answer/DeleteGroomingTestAnswerTest.java @@ -0,0 +1,84 @@ +package com.ftm.server.grooming.answer; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestAnswerMapper; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; +import com.ftm.server.adapter.out.persistence.model.GroomingTestQuestionJpaEntity; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestAnswerRepository; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestAnswer; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class DeleteGroomingTestAnswerTest extends BaseTest { + + @Autowired private GroomingTestQuestionRepository groomingTestQuestionRepository; + @Autowired private GroomingTestQuestionMapper groomingTestQuestionMapper; + @Autowired private GroomingTestAnswerRepository groomingTestAnswerRepository; + @Autowired private GroomingTestAnswerMapper groomingTestAnswerMapper; + + private Long savedAnswerId; + + private ResultActions getResultActions(Long answerId, MockHttpSession session) + throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.delete( + "/api/grooming/tests/answers/{answerId}", answerId) + .session(session)); + } + + @BeforeEach + void setUp() { + SaveGroomingTestQuestionCommand questionCommand = + SaveGroomingTestQuestionCommand.of(GroomingCategory.BEAUTY, "그루밍 테스트 질문"); + GroomingTestQuestion question = GroomingTestQuestion.create(questionCommand); + GroomingTestQuestionJpaEntity questionJpaEntity = + groomingTestQuestionRepository.save( + groomingTestQuestionMapper.toJpaEntity(question)); + + SaveGroomingTestAnswerCommand answerCommand = + SaveGroomingTestAnswerCommand.of(questionJpaEntity.getId(), "그루밍 테스트 답변", 1); + GroomingTestAnswer answer = GroomingTestAnswer.create(answerCommand); + savedAnswerId = + groomingTestAnswerRepository + .save(groomingTestAnswerMapper.toJpaEntity(answer, questionJpaEntity)) + .getId(); + } + + @Test + @Transactional + void 그루밍_테스트_답변_삭제_성공() throws Exception { + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(savedAnswerId, session); + + // then + resultActions.andExpect(status().isOk()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_삭제_실패() throws Exception { + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(savedAnswerId, session); + + // then + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } +} diff --git a/src/test/java/com/ftm/server/grooming/answer/SaveGroomingTestAnswerTest.java b/src/test/java/com/ftm/server/grooming/answer/SaveGroomingTestAnswerTest.java new file mode 100644 index 0000000..1909a03 --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/answer/SaveGroomingTestAnswerTest.java @@ -0,0 +1,101 @@ +package com.ftm.server.grooming.answer; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestAnswerRequest; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class SaveGroomingTestAnswerTest extends BaseTest { + + @Autowired private GroomingTestQuestionRepository groomingTestQuestionRepository; + @Autowired private GroomingTestQuestionMapper groomingTestQuestionMapper; + + private Long savedQuestionId; + + private ResultActions getResultActions( + Long questionId, SaveGroomingTestAnswerRequest request, MockHttpSession session) + throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.post( + "/api/grooming/tests/questions/{questionId}/answers", questionId) + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(request)) + .session(session)); + } + + @BeforeEach + void setUp() { + SaveGroomingTestQuestionCommand command = + SaveGroomingTestQuestionCommand.of(GroomingCategory.BEAUTY, "그루밍 테스트 질문"); + GroomingTestQuestion question = GroomingTestQuestion.create(command); + savedQuestionId = + groomingTestQuestionRepository + .save(groomingTestQuestionMapper.toJpaEntity(question)) + .getId(); + } + + @Test + @Transactional + void 그루밍_테스트_답변_저장_성공() throws Exception { + // given + SaveGroomingTestAnswerRequest request = new SaveGroomingTestAnswerRequest("그루밍 테스트 답변", 1); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(savedQuestionId, request, session); + + // then + resultActions.andExpect(status().isCreated()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_저장_실패1() throws Exception { + // given + SaveGroomingTestAnswerRequest request = new SaveGroomingTestAnswerRequest("그루밍 테스트 답변", 1); + + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(savedQuestionId, request, session); + + // then + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_저장_실패2() throws Exception { + // given + SaveGroomingTestAnswerRequest request = new SaveGroomingTestAnswerRequest("그루밍 테스트 답변", 1); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(10000000L, request, session); + + // then + resultActions + .andExpect( + status().is( + ErrorResponseCode.GROOMING_TEST_QUESTION_NOT_FOUND + .getHttpStatus() + .value())) + .andDo(print()); + } +} diff --git a/src/test/java/com/ftm/server/grooming/answer/UpdateGroomingTestAnswerTest.java b/src/test/java/com/ftm/server/grooming/answer/UpdateGroomingTestAnswerTest.java new file mode 100644 index 0000000..52e111b --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/answer/UpdateGroomingTestAnswerTest.java @@ -0,0 +1,139 @@ +package com.ftm.server.grooming.answer; + +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.grooming.dto.request.UpdateGroomingTestAnswerRequest; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestAnswerMapper; +import com.ftm.server.adapter.out.persistence.mapper.GroomingTestQuestionMapper; +import com.ftm.server.adapter.out.persistence.model.GroomingTestQuestionJpaEntity; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestAnswerRepository; +import com.ftm.server.adapter.out.persistence.repository.GroomingTestQuestionRepository; +import com.ftm.server.application.command.grooming.SaveGroomingTestQuestionCommand; +import com.ftm.server.application.command.grooming.answer.SaveGroomingTestAnswerCommand; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingTestAnswer; +import com.ftm.server.domain.entity.GroomingTestQuestion; +import com.ftm.server.domain.enums.GroomingCategory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class UpdateGroomingTestAnswerTest extends BaseTest { + + @Autowired private GroomingTestQuestionRepository groomingTestQuestionRepository; + @Autowired private GroomingTestQuestionMapper groomingTestQuestionMapper; + @Autowired private GroomingTestAnswerRepository groomingTestAnswerRepository; + @Autowired private GroomingTestAnswerMapper groomingTestAnswerMapper; + + private Long savedAnswerId; + + private ResultActions getResultActions( + Long answerId, UpdateGroomingTestAnswerRequest request, MockHttpSession session) + throws Exception { + return mockMvc.perform( + RestDocumentationRequestBuilders.patch( + "/api/grooming/tests/answers/{answerId}", answerId) + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(request)) + .session(session)); + } + + @BeforeEach + void setUp() { + SaveGroomingTestQuestionCommand questionCommand = + SaveGroomingTestQuestionCommand.of(GroomingCategory.BEAUTY, "그루밍 테스트 질문"); + GroomingTestQuestion question = GroomingTestQuestion.create(questionCommand); + GroomingTestQuestionJpaEntity questionJpaEntity = + groomingTestQuestionRepository.save( + groomingTestQuestionMapper.toJpaEntity(question)); + + SaveGroomingTestAnswerCommand answerCommand = + SaveGroomingTestAnswerCommand.of(questionJpaEntity.getId(), "그루밍 테스트 답변", 1); + GroomingTestAnswer answer = GroomingTestAnswer.create(answerCommand); + savedAnswerId = + groomingTestAnswerRepository + .save(groomingTestAnswerMapper.toJpaEntity(answer, questionJpaEntity)) + .getId(); + } + + @Test + @Transactional + void 그루밍_테스트_답변_수정_성공() throws Exception { + // given + UpdateGroomingTestAnswerRequest request = + new UpdateGroomingTestAnswerRequest(null, "그루밍 테스트 답변 수정", 10); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(savedAnswerId, request, session); + + // then + resultActions.andExpect(status().isOk()).andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_수정_실패1() throws Exception { + // given + UpdateGroomingTestAnswerRequest request = + new UpdateGroomingTestAnswerRequest(null, null, 10); + + // when + MockHttpSession session = createUserAndLogin(); + ResultActions resultActions = getResultActions(savedAnswerId, request, session); + + // then + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHORIZATION.getHttpStatus().value())) + .andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_수정_실패2() throws Exception { + // given + UpdateGroomingTestAnswerRequest request = + new UpdateGroomingTestAnswerRequest(null, null, 10); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(1000000L, request, session); + + // then + resultActions + .andExpect( + status().is( + ErrorResponseCode.GROOMING_TEST_ANSWER_NOT_FOUND + .getHttpStatus() + .value())) + .andDo(print()); + } + + @Test + @Transactional + void 그루밍_테스트_답변_수정_실패3() throws Exception { + // given + UpdateGroomingTestAnswerRequest request = + new UpdateGroomingTestAnswerRequest(100000000L, null, 10); + + // when + MockHttpSession session = createAdminUserAndLogin(); + ResultActions resultActions = getResultActions(savedAnswerId, request, session); + + // then + resultActions + .andExpect( + status().is( + ErrorResponseCode.GROOMING_TEST_QUESTION_NOT_FOUND + .getHttpStatus() + .value())) + .andDo(print()); + } +}