diff --git a/src/main/java/com/fc/shimpyo_be/domain/reservationproduct/dto/request/ReservationProductRequestDto.java b/src/main/java/com/fc/shimpyo_be/domain/reservationproduct/dto/request/ReservationProductRequestDto.java index 3d4b90cc..f98c4e65 100644 --- a/src/main/java/com/fc/shimpyo_be/domain/reservationproduct/dto/request/ReservationProductRequestDto.java +++ b/src/main/java/com/fc/shimpyo_be/domain/reservationproduct/dto/request/ReservationProductRequestDto.java @@ -15,7 +15,7 @@ public record ReservationProductRequestDto( String endDate, String visitorName, String visitorPhone, - @Min(value = 0, message = "객실 이용 금액은 음수일 수 없습니다.") + @Min(value = 0, message = "객실 이용 금액은 0원 이상부터 가능합니다.") Integer price ) { } diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/controller/RoomRestController.java b/src/main/java/com/fc/shimpyo_be/domain/room/controller/RoomRestController.java new file mode 100644 index 00000000..af9eebb3 --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/controller/RoomRestController.java @@ -0,0 +1,42 @@ +package com.fc.shimpyo_be.domain.room.controller; + +import com.fc.shimpyo_be.domain.room.dto.request.GetRoomListWithProductInfoRequestDto; +import com.fc.shimpyo_be.domain.room.dto.response.RoomListWithProductInfoResponseDto; +import com.fc.shimpyo_be.domain.room.service.RoomService; +import com.fc.shimpyo_be.global.common.ResponseDto; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/rooms") +@RestController +public class RoomRestController { + + private final RoomService roomService; + + @GetMapping + public ResponseEntity> getRoomsWithProductInfo( + @Valid @RequestBody GetRoomListWithProductInfoRequestDto request + ) { + log.debug("GET /api/rooms, roomIds : {}", request.roomIds()); + return ResponseEntity + .status(HttpStatus.OK) + .body( + ResponseDto.res( + HttpStatus.OK, + new RoomListWithProductInfoResponseDto( + roomService.getRoomsWithProductInfo(request.roomIds()) + ), + "숙소 정보를 포함한 객실 정보 리스트가 정상적으로 조회되었습니다." + ) + ); + } +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/dto/request/GetRoomListWithProductInfoRequestDto.java b/src/main/java/com/fc/shimpyo_be/domain/room/dto/request/GetRoomListWithProductInfoRequestDto.java new file mode 100644 index 00000000..0c5082ec --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/dto/request/GetRoomListWithProductInfoRequestDto.java @@ -0,0 +1,13 @@ +package com.fc.shimpyo_be.domain.room.dto.request; + +import jakarta.validation.constraints.Size; +import lombok.Builder; + +import java.util.List; + +@Builder +public record GetRoomListWithProductInfoRequestDto( + @Size(min = 1, max = 3, message = "최소 1개, 최대 3개의 객실 식별자 정보가 필요합니다.") + List roomIds +) { +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomListWithProductInfoResponseDto.java b/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomListWithProductInfoResponseDto.java new file mode 100644 index 00000000..467e9774 --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomListWithProductInfoResponseDto.java @@ -0,0 +1,11 @@ +package com.fc.shimpyo_be.domain.room.dto.response; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record RoomListWithProductInfoResponseDto( + List rooms +) { +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomWithProductResponseDto.java b/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomWithProductResponseDto.java new file mode 100644 index 00000000..bcf2ad7d --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/dto/response/RoomWithProductResponseDto.java @@ -0,0 +1,20 @@ +package com.fc.shimpyo_be.domain.room.dto.response; + +import lombok.Builder; + +@Builder +public record RoomWithProductResponseDto( + Long productId, + String productName, + String productThumbnail, + String productAddress, + String productDetailAddress, + Long roomId, + String roomName, + Integer standard, + Integer capacity, + String checkIn, + String checkOut, + Long price +) { +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepository.java b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepository.java index b0478fdd..d5cda724 100644 --- a/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepository.java +++ b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepository.java @@ -3,6 +3,7 @@ import com.fc.shimpyo_be.domain.room.entity.Room; import org.springframework.data.jpa.repository.JpaRepository; -public interface RoomRepository extends JpaRepository { +public interface RoomRepository + extends JpaRepository, RoomRepositoryCustom { } diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryCustom.java b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryCustom.java new file mode 100644 index 00000000..0a68eacd --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.fc.shimpyo_be.domain.room.repository; + +import com.fc.shimpyo_be.domain.room.entity.Room; + +import java.util.List; + +public interface RoomRepositoryCustom { + + List findAllInIdsWithProductAndPrice(List roomIds); +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryImpl.java b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryImpl.java new file mode 100644 index 00000000..9c988291 --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/repository/RoomRepositoryImpl.java @@ -0,0 +1,26 @@ +package com.fc.shimpyo_be.domain.room.repository; + +import com.fc.shimpyo_be.domain.room.entity.Room; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +import static com.fc.shimpyo_be.domain.product.entity.QProduct.product; +import static com.fc.shimpyo_be.domain.room.entity.QRoom.room; +import static com.fc.shimpyo_be.domain.room.entity.QRoomPrice.roomPrice; + +@RequiredArgsConstructor +public class RoomRepositoryImpl implements RoomRepositoryCustom { + + private final JPAQueryFactory jpaQueryFactory; + + @Override + public List findAllInIdsWithProductAndPrice(List roomIds) { + return jpaQueryFactory.selectFrom(room) + .join(room.product, product).fetchJoin() + .join(room.price, roomPrice).fetchJoin() + .where(room.id.in(roomIds)) + .fetch(); + } +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/service/RoomService.java b/src/main/java/com/fc/shimpyo_be/domain/room/service/RoomService.java new file mode 100644 index 00000000..b843d48a --- /dev/null +++ b/src/main/java/com/fc/shimpyo_be/domain/room/service/RoomService.java @@ -0,0 +1,30 @@ +package com.fc.shimpyo_be.domain.room.service; + +import com.fc.shimpyo_be.domain.room.dto.response.RoomWithProductResponseDto; +import com.fc.shimpyo_be.domain.room.repository.RoomRepository; +import com.fc.shimpyo_be.domain.room.util.RoomMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class RoomService { + + private final RoomRepository roomRepository; + + @Transactional(readOnly = true) + public List getRoomsWithProductInfo(List roomIds) { + log.debug("{} ::: {}", getClass().getSimpleName(), "getRoomsWithProductInfo"); + + return roomRepository.findAllInIdsWithProductAndPrice(roomIds) + .stream() + .map(RoomMapper::toRoomWithProductResponse) + .toList(); + } + +} diff --git a/src/main/java/com/fc/shimpyo_be/domain/room/util/RoomMapper.java b/src/main/java/com/fc/shimpyo_be/domain/room/util/RoomMapper.java index f9ecb1dc..c07f475e 100644 --- a/src/main/java/com/fc/shimpyo_be/domain/room/util/RoomMapper.java +++ b/src/main/java/com/fc/shimpyo_be/domain/room/util/RoomMapper.java @@ -1,9 +1,13 @@ package com.fc.shimpyo_be.domain.room.util; +import com.fc.shimpyo_be.domain.product.entity.Address; +import com.fc.shimpyo_be.domain.product.entity.Product; import com.fc.shimpyo_be.domain.room.dto.response.RoomOptionResponse; import com.fc.shimpyo_be.domain.room.dto.response.RoomResponse; +import com.fc.shimpyo_be.domain.room.dto.response.RoomWithProductResponseDto; import com.fc.shimpyo_be.domain.room.entity.Room; import com.fc.shimpyo_be.domain.room.entity.RoomOption; +import com.fc.shimpyo_be.global.util.DateTimeUtil; import com.fc.shimpyo_be.global.util.PricePickerByDateUtil; public class RoomMapper { @@ -46,4 +50,24 @@ public static RoomOptionResponse toRoomOptionResponse(RoomOption roomOption) { .refrigerator(roomOption.isRefrigerator()) .build(); } + + public static RoomWithProductResponseDto toRoomWithProductResponse(Room room) { + Product product = room.getProduct(); + Address productAddress = product.getAddress(); + + return RoomWithProductResponseDto.builder() + .productId(product.getId()) + .productName(product.getName()) + .productThumbnail(product.getThumbnail()) + .productAddress(productAddress.getAddress()) + .productDetailAddress(productAddress.getDetailAddress()) + .roomId(room.getId()) + .roomName(room.getName()) + .standard(room.getStandard()) + .capacity(room.getCapacity()) + .checkIn(DateTimeUtil.toString(room.getCheckIn())) + .checkOut(DateTimeUtil.toString(room.getCheckOut())) + .price(PricePickerByDateUtil.getPrice(room)) + .build(); + } } diff --git a/src/test/java/com/fc/shimpyo_be/domain/room/docs/RoomRestControllerDocsTest.java b/src/test/java/com/fc/shimpyo_be/domain/room/docs/RoomRestControllerDocsTest.java new file mode 100644 index 00000000..a0469ae5 --- /dev/null +++ b/src/test/java/com/fc/shimpyo_be/domain/room/docs/RoomRestControllerDocsTest.java @@ -0,0 +1,127 @@ +package com.fc.shimpyo_be.domain.room.docs; + +import com.fc.shimpyo_be.config.RestDocsSupport; +import com.fc.shimpyo_be.domain.room.dto.request.GetRoomListWithProductInfoRequestDto; +import com.fc.shimpyo_be.domain.room.dto.response.RoomWithProductResponseDto; +import com.fc.shimpyo_be.domain.room.service.RoomService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.constraints.ConstraintDescriptions; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.security.test.context.support.WithMockUser; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.snippet.Attributes.key; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class RoomRestControllerDocsTest extends RestDocsSupport { + + @MockBean + private RoomService roomService; + + private final ConstraintDescriptions getRoomListWithProductInfoDescriptions + = new ConstraintDescriptions(GetRoomListWithProductInfoRequestDto.class); + + @WithMockUser(roles = "USER") + @DisplayName("getRoomsWithProductInfo()는 숙소 정보를 포함한 객실 정보 리스트를 조회할 수 있다.") + @Test + void getRoomsWithProductInfo() throws Exception { + //given + String requestUrl = "/api/rooms"; + List roomIds = List.of(1L, 3L, 4L); + GetRoomListWithProductInfoRequestDto requestDto = new GetRoomListWithProductInfoRequestDto(roomIds); + + List rooms = List.of( + RoomWithProductResponseDto.builder() + .productId(1L) + .productName("호텔1") + .productThumbnail("호텔1 썸네일") + .productAddress("호텔1 주소") + .productDetailAddress("호텔1 상세 주소") + .roomId(1L) + .roomName("객실1") + .standard(2) + .capacity(4) + .checkIn("14:00") + .checkOut("12:00") + .price(80000L) + .build(), + RoomWithProductResponseDto.builder() + .productId(2L) + .productName("호텔2") + .productThumbnail("호텔2 썸네일") + .productAddress("호텔2 주소") + .productDetailAddress("호텔2 상세 주소") + .roomId(3L) + .roomName("객실3") + .standard(2) + .capacity(4) + .checkIn("14:00") + .checkOut("11:30") + .price(95000L) + .build(), + RoomWithProductResponseDto.builder() + .productId(3L) + .productName("호텔3") + .productThumbnail("호텔3 썸네일") + .productAddress("호텔3 주소") + .productDetailAddress("호텔3 상세 주소") + .roomId(4L) + .roomName("객실4") + .standard(2) + .capacity(4) + .checkIn("13:00") + .checkOut("11:00") + .price(80000L) + .build() + ); + + given(roomService.getRoomsWithProductInfo(roomIds)) + .willReturn(rooms); + + //when & then + mockMvc.perform( + get(requestUrl) + .content(objectMapper.writeValueAsString(requestDto)) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8) + ) + .andExpect(status().isOk()) + .andDo(restDoc.document( + requestFields( + fieldWithPath("roomIds").type(JsonFieldType.ARRAY).description("조회할 객실 식별자 리스트") + .attributes(key("constraints").value( + getRoomListWithProductInfoDescriptions.descriptionsForProperty("roomIds"))) + ), + responseFields(responseCommon()).and( + fieldWithPath("data").type(JsonFieldType.OBJECT).description("응답 데이터"), + fieldWithPath("data.rooms").type(JsonFieldType.ARRAY).description("조회한 객실 정보 리스트"), + fieldWithPath("data.rooms[].productId").type(JsonFieldType.NUMBER).description("숙소 식별자"), + fieldWithPath("data.rooms[].productName").type(JsonFieldType.STRING).description("숙소명"), + fieldWithPath("data.rooms[].productThumbnail").type(JsonFieldType.STRING).description("숙소 썸네일 이미지 URL"), + fieldWithPath("data.rooms[].productAddress").type(JsonFieldType.STRING).description("숙소 주소"), + fieldWithPath("data.rooms[].productDetailAddress").type(JsonFieldType.STRING).description("숙소 상세 주소"), + fieldWithPath("data.rooms[].roomId").type(JsonFieldType.NUMBER).description("객실 식별자"), + fieldWithPath("data.rooms[].roomName").type(JsonFieldType.STRING).description("객실명"), + fieldWithPath("data.rooms[].standard").type(JsonFieldType.NUMBER).description("기준 인원"), + fieldWithPath("data.rooms[].capacity").type(JsonFieldType.NUMBER).description("최대 인원"), + fieldWithPath("data.rooms[].checkIn").type(JsonFieldType.STRING).description("체크인 시간"), + fieldWithPath("data.rooms[].checkOut").type(JsonFieldType.STRING).description("체크아웃 시간"), + fieldWithPath("data.rooms[].price").type(JsonFieldType.NUMBER).description("객실 가격") + ) + ) + ); + + verify(roomService, times(1)).getRoomsWithProductInfo(roomIds); + } +} diff --git a/src/test/java/com/fc/shimpyo_be/domain/room/unit/controller/RoomRestControllerTest.java b/src/test/java/com/fc/shimpyo_be/domain/room/unit/controller/RoomRestControllerTest.java new file mode 100644 index 00000000..6e6f08ca --- /dev/null +++ b/src/test/java/com/fc/shimpyo_be/domain/room/unit/controller/RoomRestControllerTest.java @@ -0,0 +1,144 @@ +package com.fc.shimpyo_be.domain.room.unit.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fc.shimpyo_be.config.AbstractContainersSupport; +import com.fc.shimpyo_be.domain.room.dto.request.GetRoomListWithProductInfoRequestDto; +import com.fc.shimpyo_be.domain.room.dto.response.RoomWithProductResponseDto; +import com.fc.shimpyo_be.domain.room.service.RoomService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.hamcrest.Matchers.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +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.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +public class RoomRestControllerTest extends AbstractContainersSupport { + + private MockMvc mockMvc; + + @MockBean + private RoomService roomService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @BeforeEach + void setUp(@Autowired WebApplicationContext applicationContext) { + this.mockMvc = MockMvcBuilders + .webAppContextSetup(applicationContext) + .apply(springSecurity()) + .alwaysDo(print()) + .build(); + } + + @WithMockUser(roles = "USER") + @DisplayName("[api][GET][정상] 숙소 정보 포함 객실 정보 리스트 조회 API 테스트") + @Test + void getRoomsWithProductInfo_api_test() throws Exception { + //given + String requestUrl = "/api/rooms"; + List roomIds = List.of(1L, 3L, 4L); + GetRoomListWithProductInfoRequestDto requestDto = new GetRoomListWithProductInfoRequestDto(roomIds); + + List rooms = List.of( + RoomWithProductResponseDto.builder() + .productId(1L) + .productName("호텔1") + .productThumbnail("호텔1 썸네일") + .productAddress("호텔1 주소") + .productDetailAddress("호텔1 상세 주소") + .roomId(1L) + .roomName("객실1") + .standard(2) + .capacity(4) + .checkIn("14:00") + .checkOut("12:00") + .price(80000L) + .build(), + RoomWithProductResponseDto.builder() + .productId(2L) + .productName("호텔2") + .productThumbnail("호텔2 썸네일") + .productAddress("호텔2 주소") + .productDetailAddress("호텔2 상세 주소") + .roomId(3L) + .roomName("객실3") + .standard(2) + .capacity(4) + .checkIn("14:00") + .checkOut("11:30") + .price(95000L) + .build(), + RoomWithProductResponseDto.builder() + .productId(3L) + .productName("호텔3") + .productThumbnail("호텔3 썸네일") + .productAddress("호텔3 주소") + .productDetailAddress("호텔3 상세 주소") + .roomId(4L) + .roomName("객실4") + .standard(2) + .capacity(4) + .checkIn("13:00") + .checkOut("11:00") + .price(95000L) + .build() + ); + + given(roomService.getRoomsWithProductInfo(roomIds)) + .willReturn(rooms); + + //when & then + mockMvc.perform( + get(requestUrl) + .content(objectMapper.writeValueAsString(requestDto)) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8) + ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code", is(200))) + .andExpect(jsonPath("$.data").isNotEmpty()); + + verify(roomService, times(1)).getRoomsWithProductInfo(roomIds); + } + + @WithMockUser(roles = "USER") + @DisplayName("[api][GET][실패] 숙소 정보 포함 객실 정보 리스트 조회 API 테스트 - 요청 데이터 검증 에러") + @Test + void getRoomsWithProductInfo_api_request_validation_fail_test() throws Exception { + //given + String requestUrl = "/api/rooms"; + List roomIds = List.of(1L, 3L, 4L, 5L); + GetRoomListWithProductInfoRequestDto requestDto = new GetRoomListWithProductInfoRequestDto(roomIds); + + //when & then + mockMvc.perform( + get(requestUrl) + .content(objectMapper.writeValueAsString(requestDto)) + .contentType(MediaType.APPLICATION_JSON) + .characterEncoding(StandardCharsets.UTF_8) + ) + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("$.code", is(400))); + + verify(roomService, times(0)).getRoomsWithProductInfo(roomIds); + } +} diff --git a/src/test/java/com/fc/shimpyo_be/domain/room/unit/repository/RoomRepositoryTest.java b/src/test/java/com/fc/shimpyo_be/domain/room/unit/repository/RoomRepositoryTest.java new file mode 100644 index 00000000..7956401c --- /dev/null +++ b/src/test/java/com/fc/shimpyo_be/domain/room/unit/repository/RoomRepositoryTest.java @@ -0,0 +1,153 @@ +package com.fc.shimpyo_be.domain.room.unit.repository; + +import com.fc.shimpyo_be.config.DatabaseCleanUp; +import com.fc.shimpyo_be.config.TestDBCleanerConfig; +import com.fc.shimpyo_be.config.TestQuerydslConfig; +import com.fc.shimpyo_be.domain.product.entity.*; +import com.fc.shimpyo_be.domain.product.repository.ProductRepository; +import com.fc.shimpyo_be.domain.room.entity.Room; +import com.fc.shimpyo_be.domain.room.entity.RoomOption; +import com.fc.shimpyo_be.domain.room.entity.RoomPrice; +import com.fc.shimpyo_be.domain.room.repository.RoomRepository; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalTime; +import java.util.LinkedList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@Slf4j +@Import({TestQuerydslConfig.class, TestDBCleanerConfig.class}) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@DataJpaTest +public class RoomRepositoryTest { + + @Autowired + private RoomRepository roomRepository; + + @Autowired + private ProductRepository productRepository; + + @Autowired + private DatabaseCleanUp databaseCleanUp; + + private final String[] tableNameArray = { + "product", "room", "product_option", "address", "room_option", "amenity", "room_price" + }; + + private List roomIds; + + @BeforeEach + void setUp() { + databaseCleanUp.cleanUp(tableNameArray); + + Product product = productRepository.save( + Product.builder() + .name("호텔") + .thumbnail("호텔 썸네일 url") + .description("호텔 설명") + .starAvg(4.2f) + .category(Category.TOURIST_HOTEL) + .address( + Address.builder() + .address("호텔 주소") + .detailAddress("호텔 상세 주소") + .mapX(1.0) + .mapY(1.5) + .build() + ) + .productOption( + ProductOption.builder() + .cooking(true) + .foodPlace("음료 가능") + .parking(true) + .pickup(false) + .infoCenter("1500-0000") + .build() + ) + .amenity( + Amenity.builder() + .barbecue(false) + .beauty(true) + .beverage(true) + .fitness(true) + .bicycle(false) + .campfire(false) + .karaoke(true) + .publicBath(true) + .publicPc(true) + .seminar(false) + .sports(false) + .build() + ) + .build() + ); + + roomIds = new LinkedList<>(); + + for (int i = 1; i <= 5; i++) { + String roomName = "호텔 객실" + i; + roomIds.add( + roomRepository.save( + Room.builder() + .product(product) + .name(roomName) + .description(roomName + " 설명") + .standard(2) + .capacity(4) + .checkIn(LocalTime.of(14, 0)) + .checkOut(LocalTime.of(12, 0)) + .price( + RoomPrice.builder() + .offWeekDaysMinFee(75000) + .offWeekendMinFee(85000) + .peakWeekDaysMinFee(100000) + .peakWeekendMinFee(120000) + .build() + ) + .roomOption( + RoomOption.builder() + .cooking(true) + .airCondition(true) + .bath(true) + .bathFacility(true) + .pc(false) + .diningTable(true) + .hairDryer(true) + .homeTheater(false) + .internet(true) + .cable(false) + .refrigerator(true) + .sofa(true) + .toiletries(true) + .tv(true) + .build() + ) + .build() + ).getId() + ); + } + } + + @DisplayName("findAllInIdsWithProductAndPrice 테스트") + @Test + void findAllInIdsWithProductAndPrice() { + //given + + //when + List result = roomRepository.findAllInIdsWithProductAndPrice(roomIds); + + //then + assertThat(result).hasSize(5); + assertThat(result.get(0).getProduct().getId()).isEqualTo(1); + assertThat(result.get(0).getPrice()).isNotNull(); + } +} diff --git a/src/test/java/com/fc/shimpyo_be/domain/room/unit/service/RoomServiceTest.java b/src/test/java/com/fc/shimpyo_be/domain/room/unit/service/RoomServiceTest.java new file mode 100644 index 00000000..0bd36745 --- /dev/null +++ b/src/test/java/com/fc/shimpyo_be/domain/room/unit/service/RoomServiceTest.java @@ -0,0 +1,134 @@ +package com.fc.shimpyo_be.domain.room.unit.service; + +import com.fc.shimpyo_be.domain.product.entity.*; +import com.fc.shimpyo_be.domain.room.dto.response.RoomWithProductResponseDto; +import com.fc.shimpyo_be.domain.room.entity.Room; +import com.fc.shimpyo_be.domain.room.entity.RoomOption; +import com.fc.shimpyo_be.domain.room.entity.RoomPrice; +import com.fc.shimpyo_be.domain.room.repository.RoomRepository; +import com.fc.shimpyo_be.domain.room.service.RoomService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class RoomServiceTest { + + @InjectMocks + private RoomService roomService; + + @Mock + private RoomRepository roomRepository; + + @DisplayName("객실 식별자 리스트에 해당하는 객실과 숙소 정보를 리스트로 반환한다.") + @Test + void getRoomsWithProductInfo_test() { + //given + List roomIds = List.of(1L, 3L, 4L); + + Product product = Product.builder() + .name("호텔") + .thumbnail("호텔 썸네일 url") + .description("호텔 설명") + .starAvg(4.2f) + .category(Category.TOURIST_HOTEL) + .address( + Address.builder() + .address("호텔 주소") + .detailAddress("호텔 상세 주소") + .mapX(1.0) + .mapY(1.5) + .build() + ) + .productOption( + ProductOption.builder() + .cooking(true) + .foodPlace("음료 가능") + .parking(true) + .pickup(false) + .infoCenter("1500-0000") + .build() + ) + .amenity( + Amenity.builder() + .barbecue(false) + .beauty(true) + .beverage(true) + .fitness(true) + .bicycle(false) + .campfire(false) + .karaoke(true) + .publicBath(true) + .publicPc(true) + .seminar(false) + .sports(false) + .build() + ) + .build(); + + List rooms = new ArrayList<>(); + + for (int i = 1; i <= 3; i++) { + String roomName = "호텔 객실" + i; + rooms.add( + Room.builder() + .product(product) + .name(roomName) + .description(roomName + " 설명") + .standard(2) + .capacity(4) + .checkIn(LocalTime.of(14, 0)) + .checkOut(LocalTime.of(12, 0)) + .price( + RoomPrice.builder() + .offWeekDaysMinFee(75000) + .offWeekendMinFee(85000) + .peakWeekDaysMinFee(100000) + .peakWeekendMinFee(120000) + .build() + ) + .roomOption( + RoomOption.builder() + .cooking(true) + .airCondition(true) + .bath(true) + .bathFacility(true) + .pc(false) + .diningTable(true) + .hairDryer(true) + .homeTheater(false) + .internet(true) + .cable(false) + .refrigerator(true) + .sofa(true) + .toiletries(true) + .tv(true) + .build() + ) + .build() + ); + } + + given(roomRepository.findAllInIdsWithProductAndPrice(roomIds)).willReturn(rooms); + + //when + List result = roomService.getRoomsWithProductInfo(roomIds); + + //then + assertThat(result).hasSize(3); + + verify(roomRepository, times(1)).findAllInIdsWithProductAndPrice(roomIds); + } +}