diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/CurationImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/CurationImageService.java index a1d77bb6..518c2a58 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/CurationImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/CurationImageService.java @@ -40,7 +40,7 @@ protected String getSubPath() { @Override protected CurationImage toEntity(ImageRequest imageRequest) { return CurationImage.builder() - .name(imageRequest.getUrl()) + .url(imageRequest.getUrl()) .size(imageRequest.getSize()) .build(); } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/EventImageService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/EventImageService.java index 13367b66..14e0fbd9 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/EventImageService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/application/EventImageService.java @@ -44,7 +44,7 @@ protected String getSubPath() { @Override protected EventImage toEntity(ImageRequest imageRequest) { - return EventImage.builder().name(imageRequest.getUrl()).size(imageRequest.getSize()).build(); + return EventImage.builder().url(imageRequest.getUrl()).size(imageRequest.getSize()).build(); } @Transactional diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/CurationImage.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/CurationImage.java index eeadc507..aee7d07d 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/CurationImage.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/CurationImage.java @@ -19,7 +19,7 @@ public class CurationImage extends Image { private CurationCard curationCard; @Builder - public CurationImage(String name, Long size, String extension) { - super(name, size, extension); + public CurationImage(String url, Long size, String extension) { + super(url, size, extension); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/EventImage.java b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/EventImage.java index f559dd9c..ce8d1265 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/EventImage.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/Image/persist/EventImage.java @@ -19,7 +19,7 @@ public class EventImage extends Image { private Event event; @Builder - public EventImage(String name, Long size, String extension) { - super(name, size, extension); + public EventImage(String url, Long size, String extension) { + super(url, size, extension); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/api/SliceInfo.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/api/SliceInfo.java index 394f4da6..303194f8 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/api/SliceInfo.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/api/SliceInfo.java @@ -1,7 +1,11 @@ package org.ktc2.cokaen.wouldyouin._common.api; import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +@Setter +@Getter @Builder public class SliceInfo { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java index 29f7a65b..f4555d4d 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/ErrorCode.java @@ -18,7 +18,7 @@ public enum ErrorCode { FAIL_TO_PAY(HttpStatus.CONFLICT.value(), "-20400", "Fail to %s"), - UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "-20400", "Unauthorized, %s id is not matched."), + UNAUTHORIZED(HttpStatus.UNAUTHORIZED.value(), "-20400", "Unauthorized, %s."), ENTITY_PARAM_IS_NULL(HttpStatus.BAD_REQUEST.value(), "-20400", "%s is null"); diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/GlobalExceptionHandler.java b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/GlobalExceptionHandler.java index 0dada627..2b505796 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/GlobalExceptionHandler.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/_common/error/GlobalExceptionHandler.java @@ -5,6 +5,7 @@ import org.ktc2.cokaen.wouldyouin._common.api.ApiResponse; import org.ktc2.cokaen.wouldyouin._common.api.ApiResponseBody; import org.ktc2.cokaen.wouldyouin._common.exception.BusinessException; +import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -21,7 +22,7 @@ public ResponseEntity> handleBusinessException(BusinessExc @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { return ApiResponse.error(ErrorCode.INVALID_INPUT_VALUE, e.getBindingResult().getFieldErrors().stream() - .map(error -> String.format("%s %s", error.getField(), error.getDefaultMessage())) + .map(DefaultMessageSourceResolvable::getDefaultMessage) .collect(Collectors.joining(","))); } } \ No newline at end of file diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/auth/AuthorizeArgumentResolver.java b/src/main/java/org/ktc2/cokaen/wouldyouin/auth/AuthorizeArgumentResolver.java index a20ade16..cef846b8 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/auth/AuthorizeArgumentResolver.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/auth/AuthorizeArgumentResolver.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.ktc2.cokaen.wouldyouin._common.exception.UnauthorizedException; import org.ktc2.cokaen.wouldyouin.auth.persist.CustomUserDetails; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; import org.springframework.core.MethodParameter; @@ -54,6 +55,6 @@ private void validateMemberType(List required, MemberType actual) { if (actual == MemberType.admin || required.contains(actual)) { return; } - throw new RuntimeException("요구된 멤버 형식과 실제 형식이 다릅니다."); + throw new UnauthorizedException("요구된 멤버 형식과 실제 형식이 다릅니다."); } } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCardRequest.java b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCardRequest.java index 38241e5c..80946d11 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCardRequest.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCardRequest.java @@ -1,16 +1,19 @@ package org.ktc2.cokaen.wouldyouin.curation.api.dto; +import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Size; import java.util.List; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage; import org.ktc2.cokaen.wouldyouin.curation.persist.CurationCard; @Getter @Builder(toBuilder = true) +@EqualsAndHashCode public class CurationCardRequest { @NotEmpty(message = "부제목은 필수입니다.") @@ -22,6 +25,11 @@ public class CurationCardRequest { private List imageIds; + @AssertTrue(message = "이미지는 최대 5개까지 등록할 수 있습니다.") + public boolean isImageSizeValid() { + return imageIds == null || imageIds.size() <= 5; + } + public CurationCard toEntity(List images) { return CurationCard.builder() .subtitle(this.subtitle) diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCreateRequest.java b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCreateRequest.java index 9e52d9b4..4e584fa6 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCreateRequest.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationCreateRequest.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotNull; import java.util.List; import lombok.Builder; +import lombok.EqualsAndHashCode; import lombok.Getter; import org.ktc2.cokaen.wouldyouin._common.vo.Area; import org.ktc2.cokaen.wouldyouin.curation.persist.Curation; @@ -15,6 +16,7 @@ @Getter @Builder(toBuilder = true) +@EqualsAndHashCode public class CurationCreateRequest { @NotEmpty(message = "제목은 필수입니다.") diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationSliceResponse.java b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationSliceResponse.java index ef4d6540..1e426d05 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationSliceResponse.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/api/dto/CurationSliceResponse.java @@ -2,8 +2,10 @@ import java.util.List; import lombok.Builder; +import lombok.Getter; import org.ktc2.cokaen.wouldyouin._common.api.SliceInfo; +@Getter @Builder public class CurationSliceResponse { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationService.java index 8e2fc83e..162b1cf9 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/curation/application/CurationService.java @@ -78,7 +78,7 @@ public CurationResponse create(Long curatorId, CurationCreateRequest curationCre public void validateCuratorId(Long curatorId, Curation curation) { if (!curatorId.equals(curation.getCurator().getId())) { - throw new UnauthorizedException("Curator"); + throw new UnauthorizedException("큐레이터 ID가 큐레이션의 큐레이터 ID와 일치하지 않습니다."); } } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java index aaab9b76..8dc290ab 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/EventController.java @@ -62,6 +62,8 @@ public ResponseEntity> getEventsByHostId( hostId, PageRequest.of(page, size), lastId)); } + // Todo: 이름으로 검색 + @GetMapping("/{eventId}") public ResponseEntity> getEventByEventId( @PathVariable("eventId") Long eventId) { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/dto/EventCreateRequest.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/dto/EventCreateRequest.java index f4739207..e276a6e2 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/dto/EventCreateRequest.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/api/dto/EventCreateRequest.java @@ -65,7 +65,7 @@ public boolean isEndTimeAfterStartTime() { @AssertTrue(message = "이미지는 최대 5개까지 등록할 수 있습니다.") public boolean isImageSizeValid() { - return imageIds.size() <= 5; + return imageIds == null || imageIds.size() <= 5; } public Event toEntity(Host host, List images) { diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java index d28fc01e..1d54cccf 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/event/application/EventService.java @@ -74,7 +74,7 @@ public EventResponse create(Long hostId, EventCreateRequest eventCreateRequest) private void validateHostId(Long hostId, Event event) { if (!hostId.equals(event.getHost().getId())) { - throw new UnauthorizedException("Host"); + throw new UnauthorizedException("호스트 ID가 행사의 호스트 ID와 일치하지 않습니다."); } } diff --git a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java index d18cefdd..c94af33a 100644 --- a/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java +++ b/src/main/java/org/ktc2/cokaen/wouldyouin/reservation/application/ReservationService.java @@ -74,7 +74,7 @@ public void delete(Long memberId, Long reservationId) { public void validateMemberId(Long memberId, Reservation reservation) { if (!memberId.equals(reservation.getMember().getId())) { - throw new UnauthorizedException("member "); + throw new UnauthorizedException("member ID가 예약의 member ID와 일치하지 않습니다."); } } } \ No newline at end of file diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationControllerUnitTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationControllerUnitTest.java index e5a85018..499a9a99 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationControllerUnitTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/curation/CurationControllerUnitTest.java @@ -1,44 +1,42 @@ package org.ktc2.cokaen.wouldyouin.curation; +import static java.lang.Math.abs; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.times; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; import java.util.List; +import java.util.Random; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.ktc2.cokaen.wouldyouin._common.api.SliceInfo; -import org.ktc2.cokaen.wouldyouin._common.config.ParamDefaults; import org.ktc2.cokaen.wouldyouin._common.vo.Area; import org.ktc2.cokaen.wouldyouin.auth.application.JwtAuthFilter; -import org.ktc2.cokaen.wouldyouin.auth.application.JwtService; import org.ktc2.cokaen.wouldyouin.curation.api.CurationController; -import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardRequest; import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCreateRequest; -import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationResponse; -import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationSliceResponse; import org.ktc2.cokaen.wouldyouin.curation.application.CurationService; -import org.ktc2.cokaen.wouldyouin.event.api.dto.EventSliceResponse; -import org.ktc2.cokaen.wouldyouin.member.application.MemberService; -import org.ktc2.cokaen.wouldyouin.member.persist.Host; +import org.ktc2.cokaen.wouldyouin.global.TestData.CurationDomain; +import org.ktc2.cokaen.wouldyouin.global.TestData.MemberDomain; +import org.ktc2.cokaen.wouldyouin.global.mockMember.WithMockCurator; +import org.ktc2.cokaen.wouldyouin.global.mockMember.WithMockHost; +import org.ktc2.cokaen.wouldyouin.global.mockMember.WithMockMember; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.PageRequest; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -46,29 +44,21 @@ @WebMvcTest(CurationController.class) class CurationControllerUnitTest { - private static Long id; - - private static Host validHost; private static ObjectMapper objectMapper; + @MockBean private CurationService curationService; @MockBean - private MemberService memberService; - @MockBean - private JwtService jwtService; - @MockBean private JwtAuthFilter jwtAuthFilter; - @MockBean - private EventSliceResponse eventSliceResponse; @Autowired private MockMvc mockMvc; @Autowired private WebApplicationContext context; + private static final long randomId = abs(new Random().nextLong()); + @BeforeAll public static void init() { - id = 3L; -// validHost = createValidHost(); objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); } @@ -81,49 +71,11 @@ public void setup() throws Exception { .build(); } - CurationSliceResponse curationSliceResponse = CurationSliceResponse.builder() - .curations(List.of()) - .slice(SliceInfo.builder() - .sliceSize(10) - .lastId(Long.MAX_VALUE) - .build() - ) - .build(); - - CurationResponse curationResponse = CurationResponse.builder() - .build(); - - private String title; - private String content; - @Valid - private List curationCards; - @NotNull(message = "지역은 필수입니다.") - private Area area; - private List hashTag; - private List eventIds; - - @NotEmpty(message = "부제목은 필수입니다.") - private String subtitle; - - @NotBlank(message = "내용은 필수입니다.") - @Size(min = 20, max = 1000, message = "내용은 20자 이상 1000자 이하입니다.") - private String content; - - CurationCreateRequest curationCreateRequest = CurationCreateRequest.builder() - .title("title") - .content("content") - .area(Area.전체) - .build(); - @Test @DisplayName("RequestParam을 통해 전달받은 지역의 큐레이션 목록을 조회한다.") - void getCurationsByAreaOrderByCreatedDateDesc0() throws Exception { - // given - given(curationService.getAllByAreaOrderByCreatedDateDesc( - Area.광주, PageRequest.of(5, 20), 100L)) - .willReturn(curationSliceResponse); - - // when + @WithMockMember + void getCurationsByAreaOrderByCreatedDateDesc1() throws Exception { + // given, when mockMvc.perform(get("/api/curations") .param("area", Area.광주.name()) .param("page", "5") @@ -138,16 +90,9 @@ void getCurationsByAreaOrderByCreatedDateDesc0() throws Exception { @Test @DisplayName("RequestParam을 통해 지역을 지정하지 않은 경우, 전체 지역의 큐레이션 목록을 조회한다.") - void getCurationsByAreaOrderByCreatedDateDesc1() throws Exception { - // given - given(curationService.getAllByAreaOrderByCreatedDateDesc( - Area.valueOf(ParamDefaults.AREA), - PageRequest.of(Integer.decode(ParamDefaults.PAGE), Integer.decode(ParamDefaults.PAGE_SIZE)), - Long.decode(ParamDefaults.LAST_ID)) - ) - .willReturn(curationSliceResponse); - - // when + @WithMockMember + void getCurationsByAreaOrderByCreatedDateDesc2() throws Exception { + // given, when mockMvc.perform(get("/api/curations")).andDo(print()) .andExpect(status().isOk()); @@ -158,68 +103,270 @@ void getCurationsByAreaOrderByCreatedDateDesc1() throws Exception { @Test @DisplayName("ReqeustParam을 통해 요청할 페이지에 대한 정보를 전달받아, 해당하는 호스트의 큐레이션 목록을 조회한다.") - void getCurationsByCuratorIdOrderByCreatedDateDesc0() throws Exception { - // given - given(curationService.getAllByCuratorIdOrderByCreatedDateDesc( - id, PageRequest.of(5, 20), 100L)) - .willReturn(curationSliceResponse); - - // when - mockMvc.perform(get("/api/curations/curators/" + id)).andDo(print()) + @WithMockMember + void getCurationsByCuratorIdOrderByCreatedDateDesc1() throws Exception { + // given, when + mockMvc.perform(get("/api/curations/curators/" + randomId) + .param("page", "5") + .param("size", "20") + .param("lastId", "100")).andDo(print()) .andExpect(status().isOk()); // then then(curationService).should(times(1)).getAllByCuratorIdOrderByCreatedDateDesc( - eq(id), eq(PageRequest.of(5, 20)), eq(100L)); + eq(randomId), eq(PageRequest.of(5, 20)), eq(100L)); } @Test @DisplayName("RequestParam을 통해 페이지 정보를 지정하지 않은 경우, 디폴트 값으로 해당하는 호스트의 큐레이션 목록을 조회한다.") - void getCurationsByCuratorIdOrderByCreatedDateDesc1() throws Exception { - // given - given(curationService.getAllByCuratorIdOrderByCreatedDateDesc( - id, - PageRequest.of(Integer.decode(ParamDefaults.PAGE), Integer.decode(ParamDefaults.PAGE_SIZE)), - Long.decode(ParamDefaults.LAST_ID))) - .willReturn(curationSliceResponse); - - // when - mockMvc.perform(get("/api/curations/curators/" + id)).andDo(print()) + @WithMockMember + void getCurationsByCuratorIdOrderByCreatedDateDesc2() throws Exception { + // given, when + mockMvc.perform(get("/api/curations/curators/" + randomId)).andDo(print()) .andExpect(status().isOk()); // then then(curationService).should(times(1)).getAllByCuratorIdOrderByCreatedDateDesc( - eq(id), eq(PageRequest.of(0, 10)), eq(Long.MAX_VALUE)); + eq(randomId), eq(PageRequest.of(0, 10)), eq(Long.MAX_VALUE)); } @Test @DisplayName("큐레이션 ID를 통해 해당하는 큐레이션을 조회한다.") + @WithMockMember void getCurationByCurationId() throws Exception { + // given, when + mockMvc.perform(get("/api/curations/" + randomId)).andDo(print()) + .andExpect(status().isOk()); + + // then + then(curationService).should(times(1)).getById(eq(randomId)); + } + + @Test + @DisplayName("리퀘스트 바디로 전달받은 정보를 통해 큐레이션을 생성한다.") + @WithMockCurator + void createCuration1() throws Exception { // given - given(curationService.getById(id)).willReturn(curationResponse); + ArgumentCaptor captor = ArgumentCaptor.forClass(CurationCreateRequest.class); + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest(); // when - mockMvc.perform(get("/api/curations/" + id)).andDo(print()) - .andExpect(status().isOk()); + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isCreated()); + + // then + then(curationService).should(times(1)).create(eq(MemberDomain.curatorId), captor.capture()); + assertThat(captor.getValue()).isEqualTo(request); + } + + @Test + @DisplayName("호스트의 권한으로는 큐레이션을 생성할 수 없다.") + @WithMockHost + void createCuration2() throws Exception { + // given, when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(CurationDomain.createValidCurationCreateRequest()))) + .andDo(print()) + .andExpect(status().isUnauthorized()); // then - then(curationService).should(times(1)).getById(eq(id)); + then(curationService).shouldHaveNoInteractions(); } -// @PostMapping -// public ResponseEntity> createCuration( -// @Valid @RequestBody CurationCreateRequest curationCreateRequest, -// @Authorize(MemberType.curator) MemberIdentifier curator) { -// return ApiResponse.created(curationService.create(curator.id(), curationCreateRequest)); -// } + @Test + @DisplayName("멤버의 권한으로는 큐레이션을 생성할 수 없다.") + @WithMockMember + void createCuration3() throws Exception { + // given, when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(CurationDomain.createValidCurationCreateRequest()))) + .andDo(print()) + .andExpect(status().isUnauthorized()); + + // then + then(curationService).shouldHaveNoInteractions(); + } @Test - @DisplayName("리퀘스트 바디로 전달받은 정보를 통해 큐레이션을 생성한다.") - void createCuration() { + @DisplayName("큐레이션 생성 시, 제목에는 빈 값이 들어갈 수 없다.") + @WithMockCurator + void createCuration4() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .title("").build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("제목은 필수입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 큐레이션 카드의 부제목에는 빈 값이 들어갈 수 없다.") + @WithMockCurator + void createCuration5() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .curationCards(List.of(CurationDomain.createValidCurationCardRequest1().toBuilder().subtitle("").build())) + .build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("부제목은 필수입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 큐레이션 카드의 내용에는 빈 값이 들어갈 수 없다.") + @WithMockCurator + void createCuration6() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .curationCards(List.of(CurationDomain.createValidCurationCardRequest1().toBuilder().content(null).build())) + .build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("내용은 필수입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 큐레이션 카드의 내용에는 빈 값이 들어갈 수 없다.") + @WithMockCurator + void createCuration7() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .curationCards(List.of(CurationDomain.createValidCurationCardRequest1().toBuilder().content(null).build())) + .build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("내용은 필수입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 큐레이션 카드의 내용은 20자 이상 1000자 이하이어야 한다.") + @WithMockCurator + void createCuration8() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .curationCards(List.of(CurationDomain.createValidCurationCardRequest1().toBuilder().content("짧은 내용").build())) + .build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("내용은 20자 이상 1000자 이하입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 각 큐레이션 카드에는 이미지를 최대 5개까지 등록할 수 있다.") + @WithMockCurator + void createCuration9() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .curationCards(List.of(CurationDomain.createValidCurationCardRequest1().toBuilder() + .imageIds(List.of(1L, 2L, 3L, 4L, 5L, 6L)).build())) + .build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("이미지는 최대 5개까지 등록할 수 있습니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); + } + + @Test + @DisplayName("큐레이션 생성 시, 지역에는 빈 값이 들어갈 수 없다.") + @WithMockCurator + void createCuration10() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder() + .area(null).build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("지역은 필수입니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); } @Test - void updateCuration() { + @DisplayName("큐레이션 생성 시, 큐레이션 카드의 개수는 1개 이상 10개 이하이어야 한다.") + @WithMockCurator + void createCuration11() throws Exception { + // given + CurationCreateRequest request = CurationDomain.createValidCurationCreateRequest().toBuilder(). + curationCards(List.of()).build(); + + // when + mockMvc.perform(post("/api/curations") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message").value("큐레이션 카드의 개수는 1개 이상 10개 이하이어야 합니다.")); + + // then + then(curationService).shouldHaveNoInteractions(); } @Test diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/event/EventControllerUnitTest.java b/src/test/java/org/ktc2/cokaen/wouldyouin/event/EventControllerUnitTest.java index f555cc06..cfada297 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/event/EventControllerUnitTest.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/event/EventControllerUnitTest.java @@ -62,7 +62,6 @@ class EventControllerUnitTest { private JwtService jwtService; @MockBean private JwtAuthFilter jwtAuthFilter; - @MockBean private EventSliceResponse eventSliceResponse; @Autowired private MockMvc mockMvc; diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/global/TestData.java b/src/test/java/org/ktc2/cokaen/wouldyouin/global/TestData.java index a2d00f00..a8b6d04e 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/global/TestData.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/global/TestData.java @@ -2,13 +2,24 @@ import java.time.LocalDateTime; import java.util.List; +import org.ktc2.cokaen.wouldyouin.Image.persist.CurationImage; +import org.ktc2.cokaen.wouldyouin.Image.persist.EventImage; import org.ktc2.cokaen.wouldyouin.Image.persist.MemberImage; import org.ktc2.cokaen.wouldyouin._common.vo.Area; import org.ktc2.cokaen.wouldyouin._common.vo.Category; import org.ktc2.cokaen.wouldyouin._common.vo.Location; -import org.ktc2.cokaen.wouldyouin.event.api.dto.EventEditRequest; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardRequest; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCardResponse; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationCreateRequest; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationEditRequest; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationResponse; +import org.ktc2.cokaen.wouldyouin.curation.api.dto.CurationSliceResponse; +import org.ktc2.cokaen.wouldyouin.curation.persist.Curation; import org.ktc2.cokaen.wouldyouin.event.api.dto.EventCreateRequest; +import org.ktc2.cokaen.wouldyouin.event.api.dto.EventEditRequest; +import org.ktc2.cokaen.wouldyouin.event.api.dto.relationResonse.CurationEventResponse; import org.ktc2.cokaen.wouldyouin.event.persist.Event; +import org.ktc2.cokaen.wouldyouin.member.application.dto.relationResponse.CurationCuratorResponse; import org.ktc2.cokaen.wouldyouin.member.persist.AccountType; import org.ktc2.cokaen.wouldyouin.member.persist.Curator; import org.ktc2.cokaen.wouldyouin.member.persist.Host; @@ -31,10 +42,35 @@ public static MemberImage createValidMemberImage(Long id) { ReflectionTestUtils.setField(ret, "id", id); return ret; } + + public static EventImage createValidEventImage(Long id) { + EventImage ret = EventImage.builder() + .url("eventImageUrl") + .size(10L) + .extension(".jpg") + .build(); + ReflectionTestUtils.setField(ret, "id", id); + return ret; + } + + public static CurationImage createValidCurationImage(Long id) { + CurationImage ret = CurationImage.builder() + .url("curationImageUrl") + .size(10L) + .extension(".jpg") + .build(); + ReflectionTestUtils.setField(ret, "id", id); + return ret; + } } public static class MemberDomain { + public static final Long memberId = 1L; + public static final Long curatorId = 5L; + public static final Long hostId = 3L; + public static final Long welcomeMemberId = 4L; + public static Member createValidMember() { MemberImage memberImage = ImageDomain.createValidMemberImage(1L); Member ret = Member.builder() @@ -104,10 +140,23 @@ public static Curator createValidCurator() { ReflectionTestUtils.setField(memberImage, "baseMember", ret); return ret; } + + public static CurationCuratorResponse createCurationCuratorResponse() { + return CurationCuratorResponse.builder() + .nickname("nick_curator_12") + .email("curator1@example.com") + .phone("010-4545-6767") + .profileImageUrl("image2.com") + .intro("큐레이터 자기소개입니다.") + .likes(0) + .hashtags(List.of("#큐레이터", "#해시태그", "#입니다")) + .build(); + } } + public static class EventDomain { - public static Event createValidEvent () { + public static Event createValidEvent() { Event validEvent = Event.builder() .title("title") .content("content") @@ -126,7 +175,7 @@ public static Event createValidEvent () { public static EventCreateRequest createValidEventCreateRequest() { return EventCreateRequest.builder() .title("title") - .content("content 조홍식씨 최소글자 20자라고 해놓고 안 지켰어요. ") + .content("content 조홍식씨 최소글자 20자라고 해놓고 안 지켰어요.") .area(Area.전체) .location(new Location(132.0, 43.0)) .startTime(LocalDateTime.of(2025, 10, 1, 9, 0)) @@ -138,7 +187,7 @@ public static EventCreateRequest createValidEventCreateRequest() { .build(); } - public static EventEditRequest createValidEventEditRequest () { + public static EventEditRequest createValidEventEditRequest() { return EventEditRequest.builder() .title("modifiedTitle") .content("modifiedContent 조홍식씨 최소글자 20자라고 해놓고 안 지켰어요. ") @@ -152,11 +201,22 @@ public static EventEditRequest createValidEventEditRequest () { .imageIds(List.of()) .build(); } + + public static CurationEventResponse createValidCurationEventResponse() { + return CurationEventResponse.builder() + .id(1L) + .title("title") + .location(new Location(132.0, 43.0)) + .thumbnailImageUrl("thumbnailImageUrl") + .hostProfileImageUrl("hostProfileImageUrl") + .hostNickname("nick_curator_12") + .build(); + } } public static class ReservationDomain { - public static ReservationRequest createValidReservationRequest () { + public static ReservationRequest createValidReservationRequest() { return ReservationRequest.builder() .eventId(1L) .price(10000) @@ -164,8 +224,8 @@ public static ReservationRequest createValidReservationRequest () { .build(); } - public static Reservation createValidReservation () { - return Reservation.builder() + public static Reservation createValidReservation() { + return Reservation.builder() .member(MemberDomain.createValidMember()) .event(EventDomain.createValidEvent()) .price(10000) @@ -174,4 +234,91 @@ public static Reservation createValidReservation () { } } + public static class CurationDomain { + + public static Curation createValidCuration() { + return Curation.builder() + .curator(MemberDomain.createValidCurator()) + .title("title") + .content("content") + .curationCards(List.of()) + .area(Area.전체) + .hashTag(List.of("#해시태그1", "#해시태그2")) + .events(List.of(EventDomain.createValidEvent())) + .build(); + } + + public static CurationCardRequest createValidCurationCardRequest1() { + return CurationCardRequest.builder() + .subtitle("부제목1") + .content("큐레이션 카드 내용1 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다.") + .imageIds(List.of(1L, 2L)) + .build(); + } + + public static CurationCardRequest createValidCurationCardRequest2() { + return CurationCardRequest.builder() + .subtitle("부제목2") + .content("큐레이션 카드 내용2 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다.") + .imageIds(List.of(3L, 4L)) + .build(); + } + + public static CurationCardResponse createCurationCardResponse1() { + return CurationCardResponse.builder() + .subtitle("부제목1") + .content("큐레이션 카드 내용1 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다.") + .imageUrls(List.of("image1.com", "image2.com")) + .build(); + } + + public static CurationCardResponse createCurationCardResponse2() { + return CurationCardResponse.builder() + .subtitle("부제목2") + .content("큐레이션 카드 내용2 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다.") + .imageUrls(List.of("image3.com", "image4.com")) + .build(); + } + + public static CurationCreateRequest createValidCurationCreateRequest() { + return CurationCreateRequest.builder() + .title("큐레이션 제목1") + .content("큐레이션 카드 내용1 입니다. 큐레이션 카드의 내용은 최소 20자 최대 1000자 입니다.") + .curationCards(List.of(createValidCurationCardRequest1())) + .area(Area.광주) + .hashTag(List.of("#광주밴드", "#전남대")) + .eventIds(List.of(1L, 2L)) + .build(); + } + + public static CurationEditRequest createValidCurationEditRequest() { + return CurationEditRequest.builder() + .title("큐레이션 제목2") + .content("큐레이션 내용2 입니다.") + .curationCards(List.of(createValidCurationCardRequest2())) + .area(Area.서울) + .hashTag(List.of("#서울밴드", "#서울대")) + .eventIds(List.of(3L, 4L)) + .build(); + } + + public static CurationResponse createValidCurationResponse() { + return CurationResponse.builder() + .curator(MemberDomain.createCurationCuratorResponse()) + .title("title") + .content("content") + .curationCards(List.of(createCurationCardResponse1())) + .area(Area.전체) + .hashTag(List.of("#해시태그1", "#해시태그2")) + .eventsInfo(List.of(EventDomain.createValidCurationEventResponse())) + .build(); + } + + public static CurationSliceResponse createValidCurationSliceResponse() { + return CurationSliceResponse.builder() + .curations(List.of(createValidCurationResponse())) + .slice(null) + .build(); + } + } } diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockCurator.java b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockCurator.java new file mode 100644 index 00000000..a548df35 --- /dev/null +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockCurator.java @@ -0,0 +1,12 @@ +package org.ktc2.cokaen.wouldyouin.global.mockMember; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import org.ktc2.cokaen.wouldyouin.global.TestData; +import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; + +@Retention(RetentionPolicy.RUNTIME) +@WithMockCustomUser(memberId = 5L, memberType = MemberType.curator) +public @interface WithMockCurator { + +} diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockHost.java b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockHost.java index 11b353f0..7cd3062a 100644 --- a/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockHost.java +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockHost.java @@ -2,6 +2,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.ktc2.cokaen.wouldyouin.global.TestData; import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; @Retention(RetentionPolicy.RUNTIME) diff --git a/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockMember.java b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockMember.java new file mode 100644 index 00000000..ac7b65d0 --- /dev/null +++ b/src/test/java/org/ktc2/cokaen/wouldyouin/global/mockMember/WithMockMember.java @@ -0,0 +1,12 @@ +package org.ktc2.cokaen.wouldyouin.global.mockMember; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import org.ktc2.cokaen.wouldyouin.global.TestData; +import org.ktc2.cokaen.wouldyouin.member.persist.MemberType; + +@Retention(RetentionPolicy.RUNTIME) +@WithMockCustomUser(memberId = 1L, memberType = MemberType.normal) +public @interface WithMockMember { + +} \ No newline at end of file