From 2dd3577ec090aaaa2e6bad9abc5fcb9a195e34ad Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 00:15:32 +0900 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20[#30]=20:=20Test=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20=EB=8F=84=EA=B5=AC=20?= =?UTF-8?q?=EC=A0=9C=EC=9E=91=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RestaurantAdminCreateRequestDto.java | 4 +- .../domain/address/entity/Address.java | 8 + .../spring_deep/_delivery/domain/ai/Ai.java | 2 + .../_delivery/domain/order/Order.java | 3 + .../domain/order/orderItem/OrderItem.java | 2 + .../domain/order/orderItem/OrderItemDto.java | 4 + .../_delivery/domain/payment/Payment.java | 13 + .../domain/restaurant/Restaurant.java | 13 +- .../restaurant/RestaurantRequestDto.java | 4 + .../restaurantAddress/RestaurantAddress.java | 2 + .../_delivery/domain/review/Review.java | 2 + .../restaurant/RestaurantAdminApiTest.java | 166 ++++++++++ .../admin/user/UserAdminApiTest.java | 98 ++++++ .../domain/menu/MenuIntegrationTest.java | 148 +++++++++ .../domain/order/OrderIntegrationTest.java | 222 +++++++++++++ .../domain/order/ReviewIntegrationTest.java | 182 +++++++++++ .../RestaurantAddressIntegrationTest.java | 219 +++++++++++++ .../restaurant/RestaurantIntegrationTest.java | 308 ++++++++++++++++++ .../_delivery/domain/user/UserApiTest.java | 71 ++++ .../testutil/TestEntityCreateTools.java | 145 +++++++++ 20 files changed, 1613 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminApiTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/order/OrderIntegrationTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/order/ReviewIntegrationTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantAddressIntegrationTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantIntegrationTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserApiTest.java create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java diff --git a/src/main/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminCreateRequestDto.java b/src/main/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminCreateRequestDto.java index ef681ea..15bf91f 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminCreateRequestDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminCreateRequestDto.java @@ -1,10 +1,10 @@ package com.sparta.spring_deep._delivery.admin.restaurant; import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant.CategoryEnum; -import lombok.Getter; +import lombok.Data; import lombok.RequiredArgsConstructor; -@Getter +@Data @RequiredArgsConstructor public class RestaurantAdminCreateRequestDto { diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/entity/Address.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/entity/Address.java index d79a154..bc05496 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/entity/Address.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/entity/Address.java @@ -14,6 +14,7 @@ import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.UuidGenerator; @@ -42,6 +43,13 @@ public class Address extends BaseEntity { @Column(name = "address_name") private String addressName; + @Builder + public Address(User user, String address, String addressName) { + this.user = user; + this.address = address; + this.addressName = addressName; + } + public Address(AddressRequestDto requestDto, User user) { super(user.getUsername()); this.user = user; diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/ai/Ai.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/ai/Ai.java index 3bf2dd9..3b17ed3 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/ai/Ai.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/ai/Ai.java @@ -11,6 +11,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.hibernate.annotations.UuidGenerator; @@ -36,6 +37,7 @@ public class Ai extends BaseEntity { @Column(nullable = false) private String response; + @Builder public Ai( Menu menu, String request, diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/Order.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/Order.java index 1227913..c22fdb8 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/Order.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/Order.java @@ -20,6 +20,7 @@ import jakarta.validation.constraints.Size; import java.math.BigDecimal; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.hibernate.annotations.JdbcTypeCode; @@ -60,6 +61,7 @@ public class Order extends BaseEntity { @Size(max = 50) private String request; + @Builder public Order(User customer, Restaurant restaurant, Address address, @NotNull @Digits(integer = 10, fraction = 2) BigDecimal totalPrice, @Size(max = 50) String request) { @@ -71,6 +73,7 @@ public Order(User customer, Restaurant restaurant, Address address, this.request = request; } + public void updateTotalPrice(BigDecimal totalPrice) { this.totalPrice = totalPrice; } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItem.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItem.java index ab076d3..add8ffe 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItem.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItem.java @@ -17,6 +17,7 @@ import jakarta.validation.constraints.NotNull; import java.math.BigDecimal; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -50,6 +51,7 @@ public class OrderItem extends BaseEntity { private BigDecimal price; + @Builder public OrderItem(Order order, Menu menu, int quantity, @NotNull @Digits(integer = 10, fraction = 2) BigDecimal price) { super(order.getCustomer().getUsername()); diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItemDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItemDto.java index b7d5adc..28ecd4b 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItemDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/order/orderItem/OrderItemDto.java @@ -3,11 +3,15 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import java.util.UUID; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Getter @Setter +@AllArgsConstructor +@NoArgsConstructor public class OrderItemDto { @NotNull diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/payment/Payment.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/payment/Payment.java index 82c3180..3f82f8d 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/payment/Payment.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/payment/Payment.java @@ -15,6 +15,7 @@ import jakarta.validation.constraints.Digits; import java.math.BigDecimal; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.hibernate.annotations.JdbcTypeCode; @@ -56,6 +57,18 @@ public Payment(String username, Order order, BigDecimal amount) { this.amount = amount; } + @Builder + public Payment(String username, + Order order, + BigDecimal amount, + PaymentStatusEnum paymentStatus) { + super(username); + this.order = order; + this.amount = amount; + this.paymentMethod = PaymentMethodEnum.CARD; + this.paymentStatus = (paymentStatus != null) ? paymentStatus : PaymentStatusEnum.PENDING; + } + public void completePayment() { this.paymentStatus = PaymentStatusEnum.COMPLETED; } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java index d1a958d..ad9aa78 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java @@ -21,6 +21,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.UuidGenerator; @@ -66,6 +67,17 @@ public Restaurant(RestaurantAdminCreateRequestDto restaurantAdminCreateRequestDt this.restaurantAddress = restaurantAddress; } + @Builder + public Restaurant(User owner, String name, CategoryEnum category, + RestaurantAddress restaurantAddress, String phone) { + super(owner.getUsername()); + this.owner = owner; + this.name = name; + this.category = category; + this.restaurantAddress = restaurantAddress; + this.phone = phone; + } + public void UpdateRestaurant(RestaurantRequestDto restaurantRequestDto, RestaurantAddress restaurantAddress, String username) { this.category = restaurantRequestDto.getCategory(); @@ -100,5 +112,4 @@ public String getLabel() { return label; } } - } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantRequestDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantRequestDto.java index c16235b..d445067 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantRequestDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantRequestDto.java @@ -3,9 +3,13 @@ import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant.CategoryEnum; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Getter +@Setter @AllArgsConstructor +@NoArgsConstructor public class RestaurantRequestDto { String name; diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/restaurantAddress/RestaurantAddress.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/restaurantAddress/RestaurantAddress.java index 5dbabea..fca0094 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/restaurantAddress/RestaurantAddress.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/restaurantAddress/RestaurantAddress.java @@ -9,6 +9,7 @@ import jakarta.persistence.Table; import java.util.Map; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.hibernate.annotations.UuidGenerator; @@ -69,6 +70,7 @@ public class RestaurantAddress extends BaseEntity { @Column(name = "buld_slno", length = 50, nullable = false) private String buldSlno; + @Builder public RestaurantAddress(Map resultsJuso, String detailAddr, String username) { super(username); this.roadAddr = resultsJuso.get("roadAddr").toString(); diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/review/Review.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/review/Review.java index a751dfc..5a3662f 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/review/Review.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/review/Review.java @@ -13,6 +13,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import java.util.UUID; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -40,6 +41,7 @@ public class Review extends BaseEntity { @Column private String comment; + @Builder public Review(Order order, User user, int rating, String comment) { super(user.getUsername()); this.order = order; diff --git a/src/test/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminApiTest.java b/src/test/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminApiTest.java new file mode 100644 index 0000000..9da4fdd --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/admin/restaurant/RestaurantAdminApiTest.java @@ -0,0 +1,166 @@ +package com.sparta.spring_deep._delivery.admin.restaurant; + +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +public class RestaurantAdminApiTest { + + @Autowired + private MockMvc mockMvc; + + // ====================== + // GET /admin/restaurants/{restaurantId} + // ====================== + static Stream getRestaurantArguments() { + return Stream.of( + // 유효한 UUID -> 200 OK + arguments("123e4567-e89b-12d3-a456-426614174000", 200), + arguments("111e4567-e89b-12d3-a456-426614174111", 200), + // UUID 형식 오류 -> 400 Bad Request + arguments("not-a-uuid", 400), + arguments("", 400), + arguments("222e4567-e89b-12d3-a456-426614174222", 200) + ); + } + + // ====================== + // POST /admin/restaurants (음식점 생성) + // ====================== + static Stream addRestaurantArguments() { + return Stream.of( + // 올바른 요청 -> 201 CREATED + arguments( + "{ \"ownerId\": \"owner1\", \"name\": \"Restaurant A\", \"category\": \"KOREAN\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam\", \"phone\": \"010-1234-5678\" }", + 201), + arguments( + "{ \"ownerId\": \"owner2\", \"name\": \"Restaurant B\", \"category\": \"JAPANESE\", \"roadAddr\": \"Tokyo\", \"detailAddr\": \"Shibuya\", \"phone\": \"03-1234-5678\" }", + 201), + // 필수 필드 누락(예: ownerId 없음) -> 400 Bad Request + arguments( + "{ \"name\": \"Restaurant C\", \"category\": \"CHINESE\", \"roadAddr\": \"Beijing\", \"detailAddr\": \"Haidian\", \"phone\": \"010-1111-2222\" }", + 400), + // 필수 필드 누락(예: name 없음) -> 400 Bad Request + arguments( + "{ \"ownerId\": \"owner1\", \"category\": \"KOREAN\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam\", \"phone\": \"010-1234-5678\" }", + 400), + // 잘못된 enum 값 -> 400 Bad Request + arguments( + "{ \"ownerId\": \"owner1\", \"name\": \"Restaurant D\", \"category\": \"INVALID\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam\", \"phone\": \"010-1234-5678\" }", + 400) + ); + } + + // ====================== + // PUT /admin/restaurants/{restaurantId} (음식점 정보 수정) + // ====================== + static Stream updateRestaurantArguments() { + return Stream.of( + // 올바른 수정 요청 -> 200 OK + arguments("123e4567-e89b-12d3-a456-426614174000", + "{ \"name\": \"Updated Restaurant A\", \"category\": \"KOREAN\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam Updated\", \"phone\": \"010-9876-5432\" }", + 200), + arguments("111e4567-e89b-12d3-a456-426614174111", + "{ \"name\": \"Updated Restaurant B\", \"category\": \"JAPANESE\", \"roadAddr\": \"Tokyo\", \"detailAddr\": \"Shibuya Updated\", \"phone\": \"03-9876-5432\" }", + 200), + // 필수 필드 누락(예: name 누락) -> 400 Bad Request + arguments("123e4567-e89b-12d3-a456-426614174000", + "{ \"category\": \"KOREAN\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam\", \"phone\": \"010-9876-5432\" }", + 400), + // 잘못된 enum 값 -> 400 Bad Request + arguments("222e4567-e89b-12d3-a456-426614174222", + "{ \"name\": \"Updated Restaurant D\", \"category\": \"INVALID\", \"roadAddr\": \"Seoul\", \"detailAddr\": \"Gangnam\", \"phone\": \"010-9876-5432\" }", + 400), + // 잘못된 restaurantId (UUID 형식 오류) -> 400 Bad Request + arguments("not-a-uuid", + "{ \"name\": \"Updated Restaurant E\", \"category\": \"CHINESE\", \"roadAddr\": \"Beijing\", \"detailAddr\": \"Chaoyang\", \"phone\": \"010-2222-3333\" }", + 400) + ); + } + + // ====================== + // DELETE /admin/restaurants/{restaurantId} (음식점 삭제) + // ====================== + static Stream deleteRestaurantArguments() { + return Stream.of( + arguments("123e4567-e89b-12d3-a456-426614174000", 200), + arguments("111e4567-e89b-12d3-a456-426614174111", 200), + arguments("not-a-uuid", 400), + arguments("", 400), + arguments("222e4567-e89b-12d3-a456-426614174222", 200) + ); + } + + // ====================== + // GET /admin/restaurants/search (음식점 검색) + // ====================== + static Stream searchRestaurantArguments() { + return Stream.of( + arguments("/admin/restaurants/search?name=Restaurant", 200), + arguments("/admin/restaurants/search?category=KOREAN", 200), + arguments("/admin/restaurants/search?roadAddr=Seoul", 200), + // 유효하지 않은 검색 조건이더라도 검색 API는 빈 결과(Page)와 함께 200 OK 반환 + arguments("/admin/restaurants/search?category=INVALID", 200), + arguments("/admin/restaurants/search", 200) + ); + } + + @ParameterizedTest + @MethodSource("getRestaurantArguments") + void testGetRestaurant(String restaurantId, int expectedStatus) throws Exception { + mockMvc.perform(get("/admin/restaurants/{restaurantId}", restaurantId)) + .andExpect(status().is(expectedStatus)); + } + + @ParameterizedTest + @MethodSource("addRestaurantArguments") + void testAddRestaurant(String requestBody, int expectedStatus) throws Exception { + mockMvc.perform(post("/admin/restaurants") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + .with(SecurityMockMvcRequestPostProcessors.user("admin").roles("ADMIN"))) + .andExpect(status().is(expectedStatus)); + } + + @ParameterizedTest + @MethodSource("updateRestaurantArguments") + void testUpdateRestaurant(String restaurantId, String requestBody, int expectedStatus) + throws Exception { + mockMvc.perform(put("/admin/restaurants/{restaurantId}", restaurantId) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + .with(SecurityMockMvcRequestPostProcessors.user("admin").roles("ADMIN"))) + .andExpect(status().is(expectedStatus)); + } + + @ParameterizedTest + @MethodSource("deleteRestaurantArguments") + void testDeleteRestaurant(String restaurantId, int expectedStatus) throws Exception { + mockMvc.perform(delete("/admin/restaurants/{restaurantId}", restaurantId) + .with(SecurityMockMvcRequestPostProcessors.user("admin").roles("ADMIN"))) + .andExpect(status().is(expectedStatus)); + } + + @ParameterizedTest + @MethodSource("searchRestaurantArguments") + void testSearchRestaurant(String url, int expectedStatus) throws Exception { + mockMvc.perform(get(url)) + .andExpect(status().is(expectedStatus)); + } +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java b/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java new file mode 100644 index 0000000..4915966 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java @@ -0,0 +1,98 @@ +package com.sparta.spring_deep._delivery.admin.user; + +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +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; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import org.junit.jupiter.api.AfterEach; +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.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class UserAdminApiTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserAdminService userAdminService; + + private User user; + private User ADMIN; + + private Authentication getAuth(User user) { + UserDetailsImpl userDetails = new UserDetailsImpl(user); + return new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + } + + @BeforeEach + void setUp() { + // 테스트용 사용자 생성 + user = User.builder() + .username("userTest") + .password("userTest1234*") + .email("testuser@test.com") + .role(UserRole.CUSTOMER) + .isPublic(IsPublic.PUBLIC) + .build(); + + // 감사 정보 설정 (실제 환경에서는 AuditorAware에 의해 자동 설정됨) + user.setCreatedBy("system"); + + // 사용자 저장 및 초기화 + user = userRepository.save(user); + } + + @AfterEach + void tearDown() { + userRepository.deleteAll(); + } + + @Test + @DisplayName("사용자 검색") + @WithMockUser(username = "admin", authorities = "ADMIN") + void searchUsers() throws Exception { + + ResultActions result = mockMvc.perform(get("/admin/users/search") + .with(authentication(getAuth(ADMIN))) + .contentType(MediaType.APPLICATION_JSON)); + + result.andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content", hasSize(1))) + .andExpect(jsonPath("$.totalElements", is(1))) + .andExpect(jsonPath("$.content[0].username", is(user.getUsername()))); + } + + +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java new file mode 100644 index 0000000..f1d9a51 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java @@ -0,0 +1,148 @@ +package com.sparta.spring_deep._delivery.domain.menu; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; +import com.sparta.spring_deep._delivery.domain.restaurant.RestaurantRepository; +import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddress; +import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddressRepository; +import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import com.sparta.spring_deep._delivery.testutil.TestEntityCreateTools; +import java.math.BigDecimal; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@ActiveProfiles("test") +@Transactional +public class MenuIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private RestaurantAddressRepository restaurantAddressRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private ObjectMapper objectMapper; + + private User owner; + private User customer; + private User otherOwner; + private Restaurant testRestaurant; + private RestaurantAddress testRestaurantAddress; + @Autowired + private MenuRepository menuRepository; + + @BeforeEach + void setUp() { + // 가게 진짜 오너 + owner = User.builder() + .username("owner") + .password("1234") + .email("owner@test.com") + .role(UserRole.OWNER) + .isPublic(IsPublic.PUBLIC) + .build(); + owner = userRepository.save(owner); + + // 다른 가게 오너 + otherOwner = User.builder() + .username("otherOwner") + .password("1234") + .email("otherOwner@test.com") + .role(UserRole.OWNER) + .isPublic(IsPublic.PUBLIC) + .build(); + otherOwner = userRepository.save(otherOwner); + + // 고객 + customer = User.builder() + .username("customer") + .password("1234") + .email("customer@test.com") + .role(UserRole.CUSTOMER) + .isPublic(IsPublic.PUBLIC) + .build(); + customer = userRepository.save(customer); + + // 테스트용 가게 주소 생성 + testRestaurantAddress = TestEntityCreateTools.createRestaurantAddress(owner.getUsername()); + restaurantAddressRepository.save(testRestaurantAddress); + + // 테스트용 가게 생성 + testRestaurant = TestEntityCreateTools.createRestaurant( + owner, + testRestaurantAddress, + owner.getUsername(), + "010-1111-1111"); + restaurantRepository.save(testRestaurant); + + + } + + private Authentication getAuth(User user) { + UserDetailsImpl userDetails = new UserDetailsImpl(user); + return new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + } + + @Test + void addMenuTest() throws Exception { + MenuRequestDto requestDto = new MenuRequestDto(); + requestDto.setName("왕감자"); + requestDto.setDescription("진짜 큰 왕감자"); + requestDto.setPrice(BigDecimal.valueOf(15000)); + requestDto.setIsHidden(false); + + MvcResult mvcResult = mockMvc.perform( + post("/api/menus/{restaurantId}", testRestaurant.getId()) + .with(authentication(getAuth(owner))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isCreated()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + MenuResponseDto responseDto = objectMapper.readValue(responseBody, MenuResponseDto.class); + + UUID addedMenuId = responseDto.getId(); + Menu savedMenu = menuRepository.findById(addedMenuId) + .orElseThrow(() -> new RuntimeException("DB에 저장된 메뉴를 찾을 수 없습니다.")); + + assertEquals("왕감자", savedMenu.getName()); + assertEquals("진짜 큰 감자", savedMenu.getDescription()); + assertEquals(BigDecimal.valueOf(20000), savedMenu.getPrice()); + assertFalse(savedMenu.getIsHidden()); + assertEquals(testRestaurant.getId(), savedMenu.getRestaurant().getId()); + } + +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/order/OrderIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/order/OrderIntegrationTest.java new file mode 100644 index 0000000..7912110 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/order/OrderIntegrationTest.java @@ -0,0 +1,222 @@ +package com.sparta.spring_deep._delivery.domain.order; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; +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.sparta.spring_deep._delivery.domain.address.entity.Address; +import com.sparta.spring_deep._delivery.domain.address.repository.AddressRepository; +import com.sparta.spring_deep._delivery.domain.menu.Menu; +import com.sparta.spring_deep._delivery.domain.menu.MenuRepository; +import com.sparta.spring_deep._delivery.domain.order.orderDetails.OrderDetailsRequestDto; +import com.sparta.spring_deep._delivery.domain.order.orderDetails.OrderDetailsResponseDto; +import com.sparta.spring_deep._delivery.domain.order.orderItem.OrderItemDto; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; +import com.sparta.spring_deep._delivery.domain.restaurant.RestaurantRepository; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class OrderIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private AddressRepository addressRepository; + + @Autowired + private MenuRepository menuRepository; + + private User owner; + private User customer; + private Address address; + private Restaurant restaurant; + private Menu menu; + + @BeforeEach + void setUp() { + // 1. 소유자와 고객 생성 + owner = new User(); + ReflectionTestUtils.setField(owner, "username", "Owner"); + owner = userRepository.save(owner); + + customer = new User(); + ReflectionTestUtils.setField(customer, "username", "Customer"); + customer = userRepository.save(customer); + + // 2. 주소 생성 + address = new Address(); + // Address 엔티티에 존재하는 필드명을 사용 (예: address, addressName) + ReflectionTestUtils.setField(address, "address", "test address"); + ReflectionTestUtils.setField(address, "addressName", "test"); + ReflectionTestUtils.setField(address, "user", customer); + address = addressRepository.save(address); + + // 3. 음식점 생성 + restaurant = new Restaurant(); + ReflectionTestUtils.setField(restaurant, "owner", owner); + ReflectionTestUtils.setField(restaurant, "name", "레스토랑"); + // category는 enum 타입이므로 enum 상수를 직접 설정 + ReflectionTestUtils.setField(restaurant, "category", Restaurant.CategoryEnum.HANSIK); + // restaurantAddress 필드가 RestaurantAddress 타입이라면 별도의 객체를 만들어 주입해야 함. + // 만약 단순 문자열이라면 아래처럼 설정: + ReflectionTestUtils.setField(restaurant, "restaurantAddress", "서울시"); + ReflectionTestUtils.setField(restaurant, "phone", "02-111-1234"); + restaurant = restaurantRepository.save(restaurant); + + // 4. 메뉴 생성 + menu = new Menu(); + // 메뉴의 경우 음식점 ID를 설정 (문자열이 아니라 UUID 혹은 해당 타입) + ReflectionTestUtils.setField(menu, "restaurantId", restaurant.getId()); + ReflectionTestUtils.setField(menu, "name", "test menu"); + ReflectionTestUtils.setField(menu, "description", "test description"); + ReflectionTestUtils.setField(menu, "price", BigDecimal.valueOf(5000)); + ReflectionTestUtils.setField(menu, "isHidden", false); + ReflectionTestUtils.setField(menu, "isDeleted", false); + menu = menuRepository.save(menu); + } + + // 인증 객체 생성 + private UsernamePasswordAuthenticationToken getAuth(User user) { + return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); + } + + // 주문 생성 요청을 위한 헬퍼 메서드 + private OrderDetailsResponseDto createOrder() throws Exception { + OrderDetailsRequestDto requestDto = new OrderDetailsRequestDto(); + // DTO에 문자열로 ID를 설정 (getter 내부에서 UUID로 변환) + ReflectionTestUtils.setField(requestDto, "restaurantId", restaurant.getId().toString()); + ReflectionTestUtils.setField(requestDto, "addressId", address.getId().toString()); + ReflectionTestUtils.setField(requestDto, "request", "testRequest"); + + // 주문 항목 생성 (menuId는 문자열) + OrderItemDto orderItemDto = new OrderItemDto(); + ReflectionTestUtils.setField(orderItemDto, "menuId", menu.getId().toString()); + ReflectionTestUtils.setField(orderItemDto, "quantity", 1); + ReflectionTestUtils.setField(requestDto, "orderItemDtos", List.of(orderItemDto)); + + String jsonRequest = objectMapper.writeValueAsString(requestDto); + + MvcResult result = mockMvc.perform(post("/api/orders") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .with(authentication(getAuth(customer)))) + .andExpect(status().isCreated()) + .andReturn(); + + String responseContent = result.getResponse().getContentAsString(); + return objectMapper.readValue(responseContent, OrderDetailsResponseDto.class); + } + + // 주문 생성 및 상세 조회 테스트 + @Test + void testCreateAndGetOrderDetails() throws Exception { + OrderDetailsResponseDto createdOrder = createOrder(); + UUID orderId = UUID.fromString(createdOrder.getId()); + + mockMvc.perform(get("/api/orders/{orderId}", orderId) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(orderId.toString())) + .andExpect(jsonPath("$.customerId").value(customer.getUsername())) + .andExpect(jsonPath("$.restaurantId").value(restaurant.getId().toString())) + .andExpect(jsonPath("$.addressId").value(address.getId().toString())) + .andExpect(jsonPath("$.request").value("testRequest")); + } + + // 주문 상태 변경 테스트 (예: PENDING → PREPARING) + @Test + void testUpdateOrderStatus() throws Exception { + OrderDetailsResponseDto createdOrder = createOrder(); + UUID orderId = UUID.fromString(createdOrder.getId()); + + mockMvc.perform(put("/api/orders/{orderId}/status", orderId) + .param("status", "PREPARING") + .with(authentication(getAuth(owner)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.orderId").value(orderId.toString())) + .andExpect(jsonPath("$.status").value("PREPARING")); + } + + // 주문 취소 테스트 + @Test + void testCancelOrder() throws Exception { + OrderDetailsResponseDto createdOrder = createOrder(); + UUID orderId = UUID.fromString(createdOrder.getId()); + + mockMvc.perform(put("/api/orders/{orderId}/cancel", orderId) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()); + } + + // 주문 삭제 테스트 + @Test + void testDeleteOrder() throws Exception { + OrderDetailsResponseDto createdOrder = createOrder(); + UUID orderId = UUID.fromString(createdOrder.getId()); + + mockMvc.perform(delete("/api/orders/{orderId}", orderId) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()); + } + + // 내 주문 내역 검색 테스트 + @Test + void testSearchMyOrders() throws Exception { + // 고객 주문 2건 생성 + createOrder(); + createOrder(); + + mockMvc.perform(get("/api/orders/me/search") + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()); + } + + // 실시간 주문 확인(폴링) 테스트 + @Test + void testPollingOrders() throws Exception { + // 주문이 존재하도록 하나 생성 + createOrder(); + + mockMvc.perform(get("/api/orders/polling") + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()); + } +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/order/ReviewIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/order/ReviewIntegrationTest.java new file mode 100644 index 0000000..7fc2f68 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/order/ReviewIntegrationTest.java @@ -0,0 +1,182 @@ +package com.sparta.spring_deep._delivery.domain.order; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; +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.sparta.spring_deep._delivery.domain.address.repository.AddressRepository; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; +import com.sparta.spring_deep._delivery.domain.restaurant.RestaurantRepository; +import com.sparta.spring_deep._delivery.domain.review.ReviewRequestDto; +import com.sparta.spring_deep._delivery.domain.review.ReviewResponseDto; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class ReviewIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private OrderRepository orderRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private AddressRepository addressRepository; + + + // 소유자, 고객, 음식점, 주문 객체를 테스트 전 생성합니다. + private User owner; + private User customer; + private Restaurant restaurant; + private Order order; + + @BeforeEach + void setUp() { + // 가게 사장님 + owner = User.builder() + .username("owner") + .password("1234") + .email("owner@test.com") + .role(UserRole.OWNER) + .isPublic(IsPublic.PUBLIC) + .build(); + owner = userRepository.save(owner); + + // 고객 + customer = User.builder() + .username("customer") + .password("1234") + .email("customer@test.com") + .role(UserRole.CUSTOMER) + .isPublic(IsPublic.PUBLIC) + .build(); + customer = userRepository.save(customer); + + + } + + // 인증 객체 생성: 테스트 시 API 호출에 사용합니다. + private UsernamePasswordAuthenticationToken getAuth(User user) { + return new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()); + } + + // 리뷰 생성 API 호출 헬퍼 메서드 + private ReviewResponseDto createReview() throws Exception { + // ReviewRequestDto 생성: 리뷰 작성에 필요한 orderId, rating, comment를 설정합니다. + ReviewRequestDto requestDto = new ReviewRequestDto(); + ReflectionTestUtils.setField(requestDto, "orderId", order.getId().toString()); + ReflectionTestUtils.setField(requestDto, "rating", 5); + ReflectionTestUtils.setField(requestDto, "comment", "Excellent service"); + + String jsonRequest = objectMapper.writeValueAsString(requestDto); + + // POST /api/reviews 엔드포인트 호출 (고객 인증) + MvcResult result = mockMvc.perform(post("/api/reviews") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest) + .with(authentication(getAuth(customer)))) + .andExpect(status().isCreated()) + .andReturn(); + + String responseContent = result.getResponse().getContentAsString(); + return objectMapper.readValue(responseContent, ReviewResponseDto.class); + } + + // 1. 리뷰 생성 및 상세 조회 테스트 + @Test + void testCreateAndGetReview() throws Exception { + // 리뷰 생성 + ReviewResponseDto createdReview = createReview(); + String reviewId = createdReview.getId().toString(); + + // GET /api/reviews/{reviewId} 엔드포인트를 호출하여 리뷰 상세 정보를 검증 + mockMvc.perform(get("/api/reviews/{reviewId}", reviewId) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(reviewId)) + .andExpect(jsonPath("$.rating").value(5)) + .andExpect(jsonPath("$.comment").value("Excellent service")); + } + + // 2. 리뷰 수정 테스트 + @Test + void testUpdateReview() throws Exception { + // 리뷰 생성 + ReviewResponseDto createdReview = createReview(); + String reviewId = createdReview.getId().toString(); + + // 수정할 내용이 담긴 ReviewRequestDto 생성 (리뷰 수정 시 orderId는 필요 없을 수 있음) + ReviewRequestDto updateDto = new ReviewRequestDto(); + ReflectionTestUtils.setField(updateDto, "rating", 4); + ReflectionTestUtils.setField(updateDto, "comment", "Good service"); + + String updateJson = objectMapper.writeValueAsString(updateDto); + + // PUT /api/reviews/{reviewId} 엔드포인트 호출하여 리뷰 수정 요청 (고객 인증) + mockMvc.perform(put("/api/reviews/{reviewId}", reviewId) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(reviewId)) + .andExpect(jsonPath("$.rating").value(4)) + .andExpect(jsonPath("$.comment").value("Good service")); + } + + // 3. 리뷰 삭제 테스트 + @Test + void testDeleteReview() throws Exception { + // 리뷰 생성 + ReviewResponseDto createdReview = createReview(); + String reviewId = createdReview.getId().toString(); + + // DELETE /api/reviews/{reviewId} 호출 (고객 인증) + mockMvc.perform(delete("/api/reviews/{reviewId}", reviewId) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()); + } + + // 4. 특정 음식점의 리뷰 검색 테스트 + @Test + void testSearchReviewsForRestaurant() throws Exception { + // 리뷰 2건 생성 + createReview(); + createReview(); + + // GET /api/reviews/{restaurantId}/search 호출하여 해당 음식점의 리뷰 목록을 조회 + mockMvc.perform(get("/api/reviews/{restaurantId}/search", restaurant.getId().toString()) + .with(authentication(getAuth(customer)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content").isArray()); + } +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantAddressIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantAddressIntegrationTest.java new file mode 100644 index 0000000..e1d5440 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantAddressIntegrationTest.java @@ -0,0 +1,219 @@ +package com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress; + +import static com.sparta.spring_deep._delivery.testutil.TestEntityCreateTools.createRestaurantAddress; +import static com.sparta.spring_deep._delivery.testutil.TestEntityCreateTools.createUser; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; +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.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import com.sparta.spring_deep._delivery.util.RestaurantAddressTools; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class RestaurantAddressIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserRepository userRepository; + + @Autowired + private RestaurantAddressRepository restaurantAddressRepository; + + private User testUser; + + @BeforeEach + void setUp() { + testUser = createUser("testuser", UserRole.OWNER); + testUser = userRepository.save(testUser); + + + } + + private UserDetailsImpl getUserDetails() { + return new UserDetailsImpl(testUser); + } + + private Authentication getAuth() { + return new UsernamePasswordAuthenticationToken( + getUserDetails(), null, getUserDetails().getAuthorities()); + } + + // ----- 생성 API (POST /api/restaurantAddresses) ----- + @Test + void testCreateRestaurantAddressSuccess() throws Exception { + // static mocking: 외부 주소 검색 호출을 가짜 값으로 반환 + try (MockedStatic mockedTools = Mockito.mockStatic( + RestaurantAddressTools.class)) { + // 가짜 응답 데이터 구성 + Map fakeResponse = new HashMap<>(); + Map common = new HashMap<>(); + common.put("totalCount", "1"); + Map juso = new HashMap<>(); + juso.put("roadAddr", "Fake RoadAddr"); + juso.put("roadAddrPart1", "Fake Part1"); + juso.put("roadAddrPart2", "Fake Part2"); + juso.put("jibunAddr", "Fake Jibun"); + juso.put("engAddr", "Fake Eng"); + juso.put("zipNo", "FakeZip"); + juso.put("siNm", "FakeSi"); + juso.put("sggNm", "FakeSgg"); + juso.put("emdNm", "FakeEmd"); + juso.put("liNm", "FakeLi"); + juso.put("rn", "FakeRn"); + juso.put("udrtYn", "N"); + juso.put("buldMnnm", "FakeMnnm"); + juso.put("buldSlno", "FakeSlno"); + List> jusoList = new ArrayList<>(); + jusoList.add(juso); + Map results = new HashMap<>(); + results.put("common", common); + results.put("juso", jusoList); + fakeResponse.put("results", results); + + mockedTools.when(() -> RestaurantAddressTools.searchAddress(Mockito.anyString())) + .thenReturn(fakeResponse); + mockedTools.when(() -> RestaurantAddressTools.validateTotalCount(fakeResponse)) + .thenReturn(juso); + + // 요청 DTO: roadAddr와 detailAddr만 전달 + RestaurantAddressCreateRequestDto createDto = new RestaurantAddressCreateRequestDto( + "Fake RoadAddr", "Detail Address"); + String jsonRequest = objectMapper.writeValueAsString(createDto); + + mockMvc.perform(post("/api/restaurantAddresses") + .with(authentication(getAuth())) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.id").exists()) + .andExpect(jsonPath("$.roadAddr").value("Fake RoadAddr")) + .andExpect(jsonPath("$.detailAddr").value("Detail Address")) + .andExpect(jsonPath("$.jibunAddr").value("Fake Jibun")) + .andExpect(jsonPath("$.engAddr").value("Fake Eng")); + } + } + + private Authentication getAuth(User user) { + UserDetailsImpl userDetails = new UserDetailsImpl(user); + return new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + } + + // ----- 단건 조회 API (GET /api/restaurantAddresses/{id}) ----- + @Test + void testGetRestaurantAddressSuccess() throws Exception { + // 테스트용 RestaurantAddress 직접 생성 (static 메서드 호출 없이 DB에 값 세팅) + RestaurantAddress address = createRestaurantAddress("testuser"); + address = restaurantAddressRepository.save(address); + + mockMvc.perform(get("/api/restaurantAddresses/{id}", address.getId()) + .with(authentication(getAuth())) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(address.getId().toString())) + .andExpect(jsonPath("$.roadAddr").value("Test Road")) + .andExpect(jsonPath("$.detailAddr").value("Test Detail")) + .andExpect(jsonPath("$.jibunAddr").value("Test Jibun")) + .andExpect(jsonPath("$.engAddr").value("Test Eng")); + } + + // ----- 수정 API (PUT /api/restaurantAddresses/{id}) ----- + @Test + void testUpdateRestaurantAddressSuccess() throws Exception { + // 먼저 기존 주소 생성 + RestaurantAddress address = createRestaurantAddress("testuser"); + address = restaurantAddressRepository.save(address); + + // static mocking: update 시에도 외부 주소 검색 호출을 대체 + try (MockedStatic mockedTools = Mockito.mockStatic( + RestaurantAddressTools.class)) { + Map fakeResponse = new HashMap<>(); + Map common = new HashMap<>(); + common.put("totalCount", "1"); + Map juso = new HashMap<>(); + juso.put("roadAddr", "Updated RoadAddr"); + juso.put("roadAddrPart1", "Updated Part1"); + juso.put("roadAddrPart2", "Updated Part2"); + juso.put("jibunAddr", "Updated Jibun"); + juso.put("engAddr", "Updated Eng"); + juso.put("zipNo", "99999"); + juso.put("siNm", "Updated Si"); + juso.put("sggNm", "Updated Sgg"); + juso.put("emdNm", "Updated Em"); + juso.put("liNm", "Updated Li"); + juso.put("rn", "Updated Rn"); + juso.put("udrtYn", "N"); + juso.put("buldMnnm", "Updated Mnnm"); + juso.put("buldSlno", "Updated Slno"); + List> jusoList = new ArrayList<>(); + jusoList.add(juso); + Map results = new HashMap<>(); + results.put("common", common); + results.put("juso", jusoList); + fakeResponse.put("results", results); + + mockedTools.when(() -> RestaurantAddressTools.searchAddress(Mockito.anyString())) + .thenReturn(fakeResponse); + mockedTools.when(() -> RestaurantAddressTools.validateTotalCount(fakeResponse)) + .thenReturn(juso); + + // 수정 요청 DTO: roadAddr와 detailAddr만 변경 요청 (나머지는 외부 API 결과로 채워짐) + RestaurantAddressCreateRequestDto updateDto = new RestaurantAddressCreateRequestDto( + "Updated RoadAddr", "Updated Detail"); + String updateJson = objectMapper.writeValueAsString(updateDto); + + mockMvc.perform(put("/api/restaurantAddresses/{id}", address.getId()) + .with(authentication(getAuth())) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.roadAddr").value("Updated RoadAddr")) + .andExpect(jsonPath("$.detailAddr").value("Updated Detail")) + .andExpect(jsonPath("$.jibunAddr").value("Updated Jibun")) + .andExpect(jsonPath("$.engAddr").value("Updated Eng")); + } + } + + // ----- 삭제 API (DELETE /api/restaurantAddresses/{id}) ----- + @Test + void testDeleteRestaurantAddressSuccess() throws Exception { + // 먼저 주소 생성 + RestaurantAddress address = createRestaurantAddress("testuser"); + address = restaurantAddressRepository.save(address); + + mockMvc.perform(delete("/api/restaurantAddresses/{id}", address.getId()) + .with(authentication(getAuth()))) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantIntegrationTest.java new file mode 100644 index 0000000..255ecc2 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/restaurant/RestaurantIntegrationTest.java @@ -0,0 +1,308 @@ +package com.sparta.spring_deep._delivery.domain.restaurant; + +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasSize; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +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.sparta.spring_deep._delivery.admin.restaurant.RestaurantAdminCreateRequestDto; +import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddress; +import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddressRepository; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +import java.util.Collections; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class RestaurantIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private RestaurantAddressRepository restaurantAddressRepository; + + @Autowired + private UserRepository userRepository; + + private User owner; + private User nonOwner; + private RestaurantAddress address; + + @BeforeEach + void setUp() { + // 테스트용 소유자 생성 + owner = new User(); + ReflectionTestUtils.setField(owner, "username", "owner1"); + owner = userRepository.save(owner); + + // 소유자가 아닌 사용자 생성 + nonOwner = new User(); + ReflectionTestUtils.setField(nonOwner, "username", "user2"); + nonOwner = userRepository.save(nonOwner); + + // 테스트용 주소 생성 + address = new RestaurantAddress(); + ReflectionTestUtils.setField(address, "roadAddr", "Test Road"); + ReflectionTestUtils.setField(address, "jibunAddr", "Test Jibun"); + ReflectionTestUtils.setField(address, "detailAddr", "Test Detail"); + ReflectionTestUtils.setField(address, "engAddr", "Test Eng"); + address = restaurantAddressRepository.save(address); + + + } + + // 인증 객체 생성 (소유자/비소유자 구분) + private UsernamePasswordAuthenticationToken getAuth(User user) { + return new UsernamePasswordAuthenticationToken(user, null, Collections.emptyList()); + } + + // ===== GET /api/restaurants/{restaurantId} ===== + + // 성공 케이스: 존재하는 음식점 조회 + @Test + void testGetRestaurantSuccess() throws Exception { + // 테스트 음식점 생성 (생성 시 ReflectionTestUtils로 DTO 필드 주입) + RestaurantAdminCreateRequestDto createDto = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto, "name", "Test Restaurant"); + ReflectionTestUtils.setField(createDto, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto, "roadAddr", address.getRoadAddr()); + ReflectionTestUtils.setField(createDto, "detailAddr", address.getDetailAddr()); + ReflectionTestUtils.setField(createDto, "phone", "010-1111-2222"); + + Restaurant restaurant = new Restaurant(createDto, owner, address, owner.getUsername()); + restaurant = restaurantRepository.save(restaurant); + + RestaurantAdminCreateRequestDto createRequestDto = new RestaurantAdminCreateRequestDto(); + createRequestDto.setOwnerId(owner.getUsername()); // 굿 + createRequestDto.setName("Test Restaurant"); + createRequestDto.setCategory(Restaurant.CategoryEnum.HANSIK); + createRequestDto.setRoadAddr(address.getRoadAddr()); + createRequestDto.setDetailAddr(address.getDetailAddr()); + createRequestDto.setPhone("010-1111-2222"); + + Restaurant restaurant1 = new Restaurant(createDto, owner, address, owner.getUsername()); + + mockMvc.perform(get("/api/restaurants/{restaurantId}", restaurant.getId()) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(restaurant.getId().toString())) + .andExpect(jsonPath("$.name").value("Test Restaurant")) + .andExpect(jsonPath("$.phone").value("010-1111-2222")) + .andExpect(jsonPath("$.roadAddr").value(address.getRoadAddr())); + } + + // 실패 케이스: 존재하지 않는 음식점 조회 -> 404 Not Found + @Test + void testGetRestaurantNotFound() throws Exception { + UUID randomId = UUID.randomUUID(); + mockMvc.perform(get("/api/restaurants/{restaurantId}", randomId) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } + + // ===== PUT /api/restaurants/{restaurantId} (음식점 수정) ===== + + // 성공 케이스: 음식점 수정 (요청자와 소유자가 일치) + @Test + void testUpdateRestaurantSuccess() throws Exception { + // 초기 음식점 생성 + RestaurantAdminCreateRequestDto createDto = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto, "name", "Original Restaurant"); + ReflectionTestUtils.setField(createDto, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto, "roadAddr", address.getRoadAddr()); + ReflectionTestUtils.setField(createDto, "detailAddr", address.getDetailAddr()); + ReflectionTestUtils.setField(createDto, "phone", "010-1111-2222"); + + Restaurant restaurant = new Restaurant(createDto, owner, address, owner.getUsername()); + restaurant = restaurantRepository.save(restaurant); + + // 수정할 내용 (RestaurantRequestDto는 setter 제공) + RestaurantRequestDto updateDto = new RestaurantRequestDto(); + updateDto.setName("Updated Restaurant"); + updateDto.setCategory(Restaurant.CategoryEnum.YANGSIK); + updateDto.setRoadAddr("Updated Road"); + updateDto.setDetailAddr("Updated Detail"); + updateDto.setPhone("010-2222-3333"); + + String updateJson = objectMapper.writeValueAsString(updateDto); + + mockMvc.perform(put("/api/restaurants/{restaurantId}", restaurant.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson) + .with(authentication(getAuth(owner)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Updated Restaurant")) + .andExpect(jsonPath("$.category").value("YANGSIK")) + .andExpect(jsonPath("$.phone").value("010-2222-3333")); + } + + // 실패 케이스: 수정 요청 시 요청자가 소유자가 아닌 경우 -> 403 Forbidden + @Test + void testUpdateRestaurantNotOwner() throws Exception { + // 음식점 생성 (소유자는 owner) + RestaurantAdminCreateRequestDto createDto = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto, "name", "Original Restaurant"); + ReflectionTestUtils.setField(createDto, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto, "roadAddr", address.getRoadAddr()); + ReflectionTestUtils.setField(createDto, "detailAddr", address.getDetailAddr()); + ReflectionTestUtils.setField(createDto, "phone", "010-1111-2222"); + + Restaurant restaurant = new Restaurant(createDto, owner, address, owner.getUsername()); + restaurant = restaurantRepository.save(restaurant); + + RestaurantRequestDto updateDto = new RestaurantRequestDto(); + updateDto.setName("Updated Restaurant"); + updateDto.setCategory(Restaurant.CategoryEnum.YANGSIK); + updateDto.setRoadAddr("Updated Road"); + updateDto.setDetailAddr("Updated Detail"); + updateDto.setPhone("010-2222-3333"); + + String updateJson = objectMapper.writeValueAsString(updateDto); + + // 소유자가 아닌 nonOwner로 수정 요청 + mockMvc.perform(put("/api/restaurants/{restaurantId}", restaurant.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson) + .with(authentication(getAuth(nonOwner)))) + .andExpect(status().isForbidden()); + } + + // 실패 케이스: 존재하지 않는 음식점 수정 -> 404 Not Found + @Test + void testUpdateRestaurantNotFound() throws Exception { + RestaurantRequestDto updateDto = new RestaurantRequestDto(); + updateDto.setName("Updated Restaurant"); + updateDto.setCategory(Restaurant.CategoryEnum.YANGSIK); + updateDto.setRoadAddr("Updated Road"); + updateDto.setDetailAddr("Updated Detail"); + updateDto.setPhone("010-2222-3333"); + + String updateJson = objectMapper.writeValueAsString(updateDto); + UUID randomId = UUID.randomUUID(); + + mockMvc.perform(put("/api/restaurants/{restaurantId}", randomId) + .contentType(MediaType.APPLICATION_JSON) + .content(updateJson) + .with(authentication(getAuth(owner)))) + .andExpect(status().isNotFound()); + } + + // ===== DELETE /api/restaurants/{restaurantId} (음식점 삭제) ===== + + // 성공 케이스: 소유자에 의한 삭제 + @Test + void testDeleteRestaurantSuccess() throws Exception { + RestaurantAdminCreateRequestDto createDto = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto, "name", "Restaurant to Delete"); + ReflectionTestUtils.setField(createDto, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto, "roadAddr", address.getRoadAddr()); + ReflectionTestUtils.setField(createDto, "detailAddr", address.getDetailAddr()); + ReflectionTestUtils.setField(createDto, "phone", "010-1111-2222"); + + Restaurant restaurant = new Restaurant(createDto, owner, address, owner.getUsername()); + restaurant = restaurantRepository.save(restaurant); + + mockMvc.perform(delete("/api/restaurants/{restaurantId}", restaurant.getId()) + .with(authentication(getAuth(owner)))) + .andExpect(status().isAccepted()); + } + + // 실패 케이스: 삭제 요청 시 소유자가 아닌 경우 -> 403 Forbidden + @Test + void testDeleteRestaurantNotOwner() throws Exception { + RestaurantAdminCreateRequestDto createDto = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto, "name", "Restaurant to Delete"); + ReflectionTestUtils.setField(createDto, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto, "roadAddr", address.getRoadAddr()); + ReflectionTestUtils.setField(createDto, "detailAddr", address.getDetailAddr()); + ReflectionTestUtils.setField(createDto, "phone", "010-1111-2222"); + + Restaurant restaurant = new Restaurant(createDto, owner, address, owner.getUsername()); + restaurant = restaurantRepository.save(restaurant); + + mockMvc.perform(delete("/api/restaurants/{restaurantId}", restaurant.getId()) + .with(authentication(getAuth(nonOwner)))) + .andExpect(status().isForbidden()); + } + + // 실패 케이스: 존재하지 않는 음식점 삭제 -> 404 Not Found + @Test + void testDeleteRestaurantNotFound() throws Exception { + UUID randomId = UUID.randomUUID(); + mockMvc.perform(delete("/api/restaurants/{restaurantId}", randomId) + .with(authentication(getAuth(owner)))) + .andExpect(status().isNotFound()); + } + + // ===== GET /api/restaurants/search (음식점 검색) ===== + + // 성공 케이스: 검색 조건에 맞는 음식점이 있는 경우 + @Test + void testSearchRestaurantSuccess() throws Exception { + // 두 개의 음식점 생성 + RestaurantAdminCreateRequestDto createDto1 = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto1, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto1, "name", "Alpha Restaurant"); + ReflectionTestUtils.setField(createDto1, "category", Restaurant.CategoryEnum.HANSIK); + ReflectionTestUtils.setField(createDto1, "roadAddr", "Road A"); + ReflectionTestUtils.setField(createDto1, "detailAddr", "Detail A"); + ReflectionTestUtils.setField(createDto1, "phone", "010-0000-0001"); + Restaurant restaurant1 = new Restaurant(createDto1, owner, address, owner.getUsername()); + restaurantRepository.save(restaurant1); + + RestaurantAdminCreateRequestDto createDto2 = new RestaurantAdminCreateRequestDto(); + ReflectionTestUtils.setField(createDto2, "ownerId", owner.getUsername()); + ReflectionTestUtils.setField(createDto2, "name", "Beta Restaurant"); + ReflectionTestUtils.setField(createDto2, "category", Restaurant.CategoryEnum.YANGSIK); + ReflectionTestUtils.setField(createDto2, "roadAddr", "Road B"); + ReflectionTestUtils.setField(createDto2, "detailAddr", "Detail B"); + ReflectionTestUtils.setField(createDto2, "phone", "010-0000-0002"); + Restaurant restaurant2 = new Restaurant(createDto2, owner, address, owner.getUsername()); + restaurantRepository.save(restaurant2); + + // name 파라미터에 "Restaurant"가 포함된 경우 검색 (검색 결과가 2건 이상이어야 함) + mockMvc.perform(get("/api/restaurants/search") + .param("name", "Restaurant") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.content", hasSize(greaterThanOrEqualTo(2)))); + } + + // 실패 케이스: 검색 결과가 없을 경우 -> 404 Not Found (서비스에서 ResourceNotFoundException 발생) + @Test + void testSearchRestaurantNoResult() throws Exception { + mockMvc.perform(get("/api/restaurants/search") + .param("name", "NonExistentRestaurant") + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isNotFound()); + } +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserApiTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserApiTest.java new file mode 100644 index 0000000..dc67391 --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/user/UserApiTest.java @@ -0,0 +1,71 @@ +package com.sparta.spring_deep._delivery.domain.user; + +import static org.junit.jupiter.params.provider.Arguments.arguments; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +public class UserApiTest { + + @Autowired + private MockMvc mockMvc; + + // 테스트 케이스별 JSON 요청과 예상 HTTP 상태 코드를 제공하는 메서드 + static Stream signupArguments() { + return Stream.of( + // 유효한 요청: 사용자 등록 성공 -> 201 CREATED + arguments( + "{ \"username\": \"user1\", \"password\": \"User1234!\", \"email\": \"user1@test.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 201), + arguments( + "{ \"username\": \"user2\", \"password\": \"User1234!\", \"email\": \"user2@test.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 201), + arguments( + "{ \"username\": \"admin\", \"password\": \"User1234!\", \"email\": \"admin@test.com\", \"role\": \"ADMIN\", \"isPublic\": \"PUBLIC\" }", + 201), + arguments( + "{ \"username\": \"owner1\", \"password\": \"User1234!\", \"email\": \"owner1@test.com\", \"role\": \"OWNER\", \"isPublic\": \"PUBLIC\" }", + 201), + arguments( + "{ \"username\": \"owner2\", \"password\": \"User1234!\", \"email\": \"owner2@test.com\", \"role\": \"OWNER\", \"isPublic\": \"PUBLIC\" }", + 201), + // 잘못된 요청: 패스워드 형식 오류 (특수문자 누락) -> 400 BAD_REQUEST + arguments( + "{ \"username\": \"testuser\", \"password\": \"User1234\", \"email\": \"testuser@test.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 400), + // 잘못된 요청: 이메일 형식 오류 -> 400 BAD_REQUEST + arguments( + "{ \"username\": \"testuser\", \"password\": \"User1234!\", \"email\": \"testusertest.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 400), + // 유효한 요청: 사용자 등록 성공 -> 201 CREATED + arguments( + "{ \"username\": \"testuser\", \"password\": \"User1234!\", \"email\": \"testuser@test.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 201), + // 잘못된 요청: 중복 등록 (이미 등록된 사용자) -> 400 BAD_REQUEST + arguments( + "{ \"username\": \"testuser\", \"password\": \"User1234!\", \"email\": \"testuser@test.com\", \"role\": \"CUSTOMER\", \"isPublic\": \"PUBLIC\" }", + 409) + ); + } + + @ParameterizedTest + @MethodSource("signupArguments") + void testUserSignup(String requestBody, int expectedStatus) throws Exception { + mockMvc.perform(post("/api/users/signup") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andExpect(status().is(expectedStatus)); + } + +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java new file mode 100644 index 0000000..101d9fe --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java @@ -0,0 +1,145 @@ +package com.sparta.spring_deep._delivery.testutil; + +import static com.sparta.spring_deep._delivery.util.RestaurantAddressTools.searchAddress; +import static com.sparta.spring_deep._delivery.util.RestaurantAddressTools.validateTotalCount; + +import com.sparta.spring_deep._delivery.domain.address.entity.Address; +import com.sparta.spring_deep._delivery.domain.ai.Ai; +import com.sparta.spring_deep._delivery.domain.menu.Menu; +import com.sparta.spring_deep._delivery.domain.order.Order; +import com.sparta.spring_deep._delivery.domain.order.orderItem.OrderItem; +import com.sparta.spring_deep._delivery.domain.payment.Payment; +import com.sparta.spring_deep._delivery.domain.payment.Payment.PaymentStatusEnum; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant.CategoryEnum; +import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddress; +import com.sparta.spring_deep._delivery.domain.review.Review; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import java.math.BigDecimal; +import java.util.Map; + +public class TestEntityCreateTools { + + // 유저 생성 + public static User createUser(String username, UserRole role) { + return User.builder() + .username(username) + .password(username) + .email(username + "@test.com") + .role(role) + .isPublic(IsPublic.PUBLIC) + .build(); + } + + public static Address createAddress(User user, String address, String addressName) { + return Address.builder() + .user(user) + .address(address) + .addressName(addressName) + .build(); + } + + // 가게 주소 + public static RestaurantAddress createRestaurantAddress(String username) { + // 주소 검색 + Map searchResultJson = searchAddress("논현로 111길 21"); + + // 주소 검색 결과 유효성 검사 후 Juso 부분 반환 + Map resultsJuso = validateTotalCount(searchResultJson); + + // 주소 검색 결과로 가게 주소 객체 생성 + return RestaurantAddress.builder() + .resultsJuso(resultsJuso) + .detailAddr("강남빌딩 202호") + .username(username) + .build(); + } + + // 가게 + public static Restaurant createRestaurant(User user, RestaurantAddress restaurantAddress, + String name, + String phone) { + return Restaurant.builder() + .owner(user) + .name(name) + .category(CategoryEnum.HANSIK) + .restaurantAddress(restaurantAddress) + .phone(phone) + .build(); + } + + // 메뉴 + public static Menu createMenu(User user, Restaurant restaurant, String menuName, + String description, + Double price) { + return Menu.builder() + .restaurant(restaurant) + .name(menuName) + .description(description) + .price(BigDecimal.valueOf(price)) + .isHidden(false) + .user(user) + .build(); + } + + // 메뉴 AI + public static Ai createAi(Menu menu, String request, String response, User user) { + return Ai.builder() + .menu(menu) + .request(request) + .response(response) + .user(user) + .build(); + } + + public static Payment createPayment( + String username, + Order order, + BigDecimal amount, + PaymentStatusEnum paymentStatus + ) { + return Payment.builder() + .username(username) + .order(order) + .amount(amount) + .paymentStatus(paymentStatus) + .build(); + + } + + // 주문 + public static Order createOrder(User customer, Restaurant restaurant, Address address, + Double totalPrice, String request) { + return Order.builder() + .customer(customer) + .restaurant(restaurant) + .address(address) + .totalPrice(BigDecimal.valueOf(totalPrice)) + .request(request) + .build(); + } + + // 주문 아이템 + public static OrderItem createOrderItem(Order order, Menu menu, int quantity, + Double price) { + return OrderItem.builder() + .order(order) + .menu(menu) + .quantity(quantity) + .price(BigDecimal.valueOf(price)) + .build(); + } + + //리뷰 + public static Review createReview(Order order, User user, int rating, String comment) { + return Review.builder() + .order(order) + .user(user) + .rating(rating) + .comment(comment) + .build(); + } + +} From a0ea2b4803864cfc1cd479435b3c464e2790c83d Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 00:29:29 +0900 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20[#30]=20:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=8F=84=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../testutil/TestEntityCreateTools.java | 84 ++++++++++++++++++- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java index 101d9fe..049d2a9 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java @@ -1,8 +1,7 @@ package com.sparta.spring_deep._delivery.testutil; -import static com.sparta.spring_deep._delivery.util.RestaurantAddressTools.searchAddress; -import static com.sparta.spring_deep._delivery.util.RestaurantAddressTools.validateTotalCount; - +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.sparta.spring_deep._delivery.domain.address.entity.Address; import com.sparta.spring_deep._delivery.domain.ai.Ai; import com.sparta.spring_deep._delivery.domain.menu.Menu; @@ -18,7 +17,14 @@ import com.sparta.spring_deep._delivery.domain.user.entity.User; import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; import java.math.BigDecimal; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; +import org.apache.logging.log4j.util.InternalException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; public class TestEntityCreateTools { @@ -142,4 +148,76 @@ public static Review createReview(Order order, User user, int rating, String com .build(); } + /** + * keyword로 주소 검색 후 Json(Map) 형태로 검색 결과를 반환합니다. + * + * @param keyword 주소 검색을 위한 검색어 + */ + public static Map searchAddress(String keyword) { + try { + // JSON 형식의 결과를 요청하기 위한 설정 + String resultType = "json"; + + // URI 빌드 (내부적으로 인코딩된 URL 생성) + String encodedUrl = UriComponentsBuilder.fromHttpUrl(restaurantAddressUrl) + .queryParam("confmKey", restaurantAddressKey) + .queryParam("currentPage", 1) + .queryParam("countPerPage", 10) + .queryParam("keyword", keyword) + .queryParam("resultType", resultType) + .toUriString(); + + // 디코딩된 URL로 변환 (API 서버가 요구하는 형식) + String decodedUrl = URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8); + System.out.println("Decoded URL: " + decodedUrl); + + // API 호출 + ResponseEntity response = restTemplate.getForEntity(decodedUrl, String.class); + String jsonResponse = response.getBody(); + + // JSON 문자열을 Map으로 파싱 + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonResponse, new TypeReference>() { + }); + } catch (Exception ex) { + throw new InternalException("API 호출 중 오류 발생: " + ex.getMessage(), ex); + } + } + + /** + * API 응답 결과에서 totalCount가 1인지 검사합니다. totalCount가 1이 아니면 IllegalStateException을 발생시킵니다. + * + * @param responseMap API 응답을 파싱한 Map 객체 + */ + public static Map validateTotalCount(Map responseMap) { + if (responseMap == null || !responseMap.containsKey("results")) { + throw new IllegalArgumentException("유효한 응답 데이터가 아닙니다."); + } + + // "results" 객체에서 "common" 추출 + Map results = (Map) responseMap.get("results"); + if (results == null || !results.containsKey("common")) { + throw new IllegalArgumentException("응답에 'common' 객체가 없습니다."); + } + + Map common = (Map) results.get("common"); + Object totalCountObj = common.get("totalCount"); + if (totalCountObj == null) { + throw new IllegalArgumentException("응답에 totalCount 값이 없습니다."); + } + + // totalCount 값은 문자열로 전달되므로 정수형으로 변환 + try { + int totalCount = Integer.parseInt(totalCountObj.toString()); + if (totalCount != 1) { + throw new IllegalStateException( + "totalCount가 1이 아닙니다. 현재 totalCount: " + totalCount); + } + } catch (NumberFormatException e) { + throw new IllegalArgumentException("totalCount가 정수형 값이 아닙니다: " + totalCountObj, e); + } + + return ((List>) results.get("juso")).get(0); + } + } From 802fc07491238add17f89355ab77e0074c4337aa Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 00:29:41 +0900 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20[#30]=20:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=8F=84=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_delivery/testutil/TestEntityCreateTools.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java index 049d2a9..a527f97 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java @@ -28,6 +28,11 @@ public class TestEntityCreateTools { + private static final RestTemplate restTemplate = new RestTemplate(); + + private static final String restaurantAddressUrl = "https://business.juso.go.kr/addrlink/addrLinkApi.do"; + private static final String restaurantAddressKey = "devU01TX0FVVEgyMDI1MDIxOTE4MzczNDExNTQ4ODQ="; + // 유저 생성 public static User createUser(String username, UserRole role) { return User.builder() From 7ceb99fd26651c00574f44d4825f6b5570cd6afa Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 00:52:58 +0900 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=90=9B=20bugfix=20[#30]=20:=20?= =?UTF-8?q?=EB=A0=88=EC=8A=A4=ED=86=A0=EB=9E=91=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=ED=95=AB=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../restaurant/CategoryEnumConverter.java | 52 +++++++++---------- .../domain/restaurant/Restaurant.java | 20 ++----- .../util/RestaurantAddressTools.java | 20 +++++-- .../testutil/TestEntityCreateTools.java | 17 +++++- 4 files changed, 60 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/CategoryEnumConverter.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/CategoryEnumConverter.java index 2ee7a76..40f8664 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/CategoryEnumConverter.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/CategoryEnumConverter.java @@ -1,26 +1,26 @@ -package com.sparta.spring_deep._delivery.domain.restaurant; - -import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant.CategoryEnum; -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; -import java.util.Arrays; - -@Converter(autoApply = true) -public class CategoryEnumConverter implements AttributeConverter { - - @Override - public String convertToDatabaseColumn(Restaurant.CategoryEnum attribute) { - return attribute == null ? null : attribute.getLabel(); - } - - @Override - public Restaurant.CategoryEnum convertToEntityAttribute(String dbData) { - if (dbData == null) { - return null; - } - return Arrays.stream(Restaurant.CategoryEnum.values()) - .filter(e -> e.getLabel().equals(dbData)) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("Unknown value: " + dbData)); - } -} +//package com.sparta.spring_deep._delivery.domain.restaurant; +// +//import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant.CategoryEnum; +//import jakarta.persistence.AttributeConverter; +//import jakarta.persistence.Converter; +//import java.util.Arrays; +// +//@Converter(autoApply = true) +//public class CategoryEnumConverter implements AttributeConverter { +// +// @Override +// public String convertToDatabaseColumn(Restaurant.CategoryEnum attribute) { +// return attribute == null ? null : attribute.getLabel(); +// } +// +// @Override +// public Restaurant.CategoryEnum convertToEntityAttribute(String dbData) { +// if (dbData == null) { +// return null; +// } +// return Arrays.stream(Restaurant.CategoryEnum.values()) +// .filter(e -> e.getLabel().equals(dbData)) +// .findFirst() +// .orElseThrow(() -> new IllegalArgumentException("Unknown value: " + dbData)); +// } +//} diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java index ad9aa78..4f0dbf1 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java @@ -8,7 +8,6 @@ import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddress; import com.sparta.spring_deep._delivery.domain.user.entity.User; import jakarta.persistence.Column; -import jakarta.persistence.Convert; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -47,7 +46,6 @@ public class Restaurant extends BaseEntity { @Size(max = 100) private String name; - @Convert(converter = CategoryEnumConverter.class) @Column(name = "category", nullable = false, columnDefinition = "p_restaurant_category_enum") private CategoryEnum category; @@ -97,19 +95,9 @@ public void UpdateRestaurant(RestaurantAdminRequestDto restaurantAdminRequestDto } public enum CategoryEnum { - HANSIK("한식"), - YANGSIK("양식"), - JUNGSIK("중식"), - ILSIK("일식"); - - private final String label; - - CategoryEnum(String label) { - this.label = label; - } - - public String getLabel() { - return label; - } + HANSIK, + YANGSIK, + JUNGSIK, + ILSIK } } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java b/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java index 459811c..92e8acb 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java @@ -6,21 +6,20 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; +import lombok.RequiredArgsConstructor; import org.apache.logging.log4j.util.InternalException; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +@RequiredArgsConstructor public class RestaurantAddressTools { private static final RestTemplate restTemplate = new RestTemplate(); - @Value("${restaurantAddress.url}") - private static String restaurantAddressUrl; - - @Value("${restaurantAddress.key}") - private static String restaurantAddressKey; + public static String restaurantAddressUrl; + public static String restaurantAddressKey; /** * keyword로 주소 검색 후 Json(Map) 형태로 검색 결과를 반환합니다. @@ -29,6 +28,7 @@ public class RestaurantAddressTools { */ public static Map searchAddress(String keyword) { try { + // JSON 형식의 결과를 요청하기 위한 설정 String resultType = "json"; @@ -94,4 +94,14 @@ public static Map validateTotalCount(Map respons return ((List>) results.get("juso")).get(0); } + @Value("${restaurantAddress.url}") + public void setRestaurantAddressUrl(String restaurantAddressUrl) { + RestaurantAddressTools.restaurantAddressUrl = restaurantAddressUrl; + } + + @Value("${restaurantAddress.url}") + public void setRestaurantAddressKey(String restaurantAddressKey) { + RestaurantAddressTools.restaurantAddressKey = restaurantAddressKey; + } + } diff --git a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java index a527f97..e4342be 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java @@ -16,12 +16,14 @@ import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; import com.sparta.spring_deep._delivery.domain.user.entity.User; import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.util.RestaurantAddressTools; import java.math.BigDecimal; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import org.apache.logging.log4j.util.InternalException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -30,8 +32,8 @@ public class TestEntityCreateTools { private static final RestTemplate restTemplate = new RestTemplate(); - private static final String restaurantAddressUrl = "https://business.juso.go.kr/addrlink/addrLinkApi.do"; - private static final String restaurantAddressKey = "devU01TX0FVVEgyMDI1MDIxOTE4MzczNDExNTQ4ODQ="; + public static String restaurantAddressUrl; + public static String restaurantAddressKey; // 유저 생성 public static User createUser(String username, UserRole role) { @@ -225,4 +227,15 @@ public static Map validateTotalCount(Map respons return ((List>) results.get("juso")).get(0); } + @Value("${restaurantAddress.url}") + public void setRestaurantAddressUrl(String restaurantAddressUrl) { + RestaurantAddressTools.restaurantAddressUrl = restaurantAddressUrl; + } + + @Value("${restaurantAddress.url}") + public void setRestaurantAddressKey(String restaurantAddressKey) { + RestaurantAddressTools.restaurantAddressKey = restaurantAddressKey; + } + + } From 3bd3e72062f9d2785820480f34164c7f40b57e87 Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 01:08:20 +0900 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=A9=B9=20fix=20[#30]=20:=20api=20hotf?= =?UTF-8?q?ix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ .../util/RestaurantAddressTools.java | 16 ++++------------ .../testutil/TestEntityCreateTools.java | 19 ++++--------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index bacbc31..4a0dd2d 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,9 @@ dependencies { annotationProcessor 'jakarta.annotation:jakarta.annotation-api' annotationProcessor 'jakarta.persistence:jakarta.persistence-api' + // dotenv + implementation 'io.github.cdimascio:java-dotenv:+' + // Hibernate implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate6:2.18.2' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2' diff --git a/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java b/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java index 92e8acb..f05c779 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/util/RestaurantAddressTools.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.cdimascio.dotenv.Dotenv; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.List; @@ -18,8 +19,9 @@ public class RestaurantAddressTools { private static final RestTemplate restTemplate = new RestTemplate(); - public static String restaurantAddressUrl; - public static String restaurantAddressKey; + private static final Dotenv dotenv = Dotenv.load(); + private static final String restaurantAddressUrl = dotenv.get("RESTAURANT_URL"); + private static final String restaurantAddressKey = dotenv.get("RESTAURANT_KEY"); /** * keyword로 주소 검색 후 Json(Map) 형태로 검색 결과를 반환합니다. @@ -94,14 +96,4 @@ public static Map validateTotalCount(Map respons return ((List>) results.get("juso")).get(0); } - @Value("${restaurantAddress.url}") - public void setRestaurantAddressUrl(String restaurantAddressUrl) { - RestaurantAddressTools.restaurantAddressUrl = restaurantAddressUrl; - } - - @Value("${restaurantAddress.url}") - public void setRestaurantAddressKey(String restaurantAddressKey) { - RestaurantAddressTools.restaurantAddressKey = restaurantAddressKey; - } - } diff --git a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java index e4342be..33fb329 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/testutil/TestEntityCreateTools.java @@ -16,14 +16,13 @@ import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; import com.sparta.spring_deep._delivery.domain.user.entity.User; import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; -import com.sparta.spring_deep._delivery.util.RestaurantAddressTools; +import io.github.cdimascio.dotenv.Dotenv; import java.math.BigDecimal; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import org.apache.logging.log4j.util.InternalException; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; @@ -32,8 +31,9 @@ public class TestEntityCreateTools { private static final RestTemplate restTemplate = new RestTemplate(); - public static String restaurantAddressUrl; - public static String restaurantAddressKey; + private static final Dotenv dotenv = Dotenv.load(); + private static final String restaurantAddressUrl = dotenv.get("RESTAURANT_URL"); + private static final String restaurantAddressKey = dotenv.get("RESTAURANT_KEY"); // 유저 생성 public static User createUser(String username, UserRole role) { @@ -227,15 +227,4 @@ public static Map validateTotalCount(Map respons return ((List>) results.get("juso")).get(0); } - @Value("${restaurantAddress.url}") - public void setRestaurantAddressUrl(String restaurantAddressUrl) { - RestaurantAddressTools.restaurantAddressUrl = restaurantAddressUrl; - } - - @Value("${restaurantAddress.url}") - public void setRestaurantAddressKey(String restaurantAddressKey) { - RestaurantAddressTools.restaurantAddressKey = restaurantAddressKey; - } - - } From b986b384a2828d43681ace617c7efdefba373a57 Mon Sep 17 00:00:00 2001 From: Bulgogi-Pizza Date: Tue, 25 Feb 2025 01:16:10 +0900 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=A9=B9=20fix=20[#30]=20:=20api=20hotf?= =?UTF-8?q?ix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring_deep/_delivery/domain/restaurant/Restaurant.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java index 4f0dbf1..d80918a 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/restaurant/Restaurant.java @@ -9,6 +9,8 @@ import com.sparta.spring_deep._delivery.domain.user.entity.User; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -23,7 +25,9 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.UuidGenerator; +import org.hibernate.type.SqlTypes; @Entity @Getter @@ -46,7 +50,9 @@ public class Restaurant extends BaseEntity { @Size(max = 100) private String name; + @Enumerated(EnumType.STRING) @Column(name = "category", nullable = false, columnDefinition = "p_restaurant_category_enum") + @JdbcTypeCode(SqlTypes.NAMED_ENUM) private CategoryEnum category; @NotNull From 7c9f7d327f0db1f7d914a79b6497a92c800c48c6 Mon Sep 17 00:00:00 2001 From: Imnotcoderdude Date: Tue, 25 Feb 2025 01:30:46 +0900 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20:=20Menu=20API=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=B6=94=EA=B0=80=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=ED=86=B5?= =?UTF-8?q?=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spring_deep/_delivery/domain/menu/MenuResponseDto.java | 2 ++ .../_delivery/domain/menu/MenuIntegrationTest.java | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuResponseDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuResponseDto.java index 272706e..515a636 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuResponseDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuResponseDto.java @@ -4,9 +4,11 @@ import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data @AllArgsConstructor +@NoArgsConstructor public class MenuResponseDto { private UUID id; diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java index f1d9a51..63d1592 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java @@ -105,7 +105,6 @@ void setUp() { "010-1111-1111"); restaurantRepository.save(testRestaurant); - } private Authentication getAuth(User user) { @@ -139,8 +138,8 @@ void addMenuTest() throws Exception { .orElseThrow(() -> new RuntimeException("DB에 저장된 메뉴를 찾을 수 없습니다.")); assertEquals("왕감자", savedMenu.getName()); - assertEquals("진짜 큰 감자", savedMenu.getDescription()); - assertEquals(BigDecimal.valueOf(20000), savedMenu.getPrice()); + assertEquals("진짜 큰 왕감자", savedMenu.getDescription()); + assertEquals(BigDecimal.valueOf(15000), savedMenu.getPrice()); assertFalse(savedMenu.getIsHidden()); assertEquals(testRestaurant.getId(), savedMenu.getRestaurant().getId()); } From d108cd8c3c648fcbf4de31f3473eb93073784846 Mon Sep 17 00:00:00 2001 From: Imnotcoderdude Date: Tue, 25 Feb 2025 02:38:16 +0900 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20:=20Menu=20API=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20-=20=EB=A9=94=EB=89=B4=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1=20-=20ai=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=84=B1=EA=B3=B5,=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_delivery/admin/ai/AiRepository.java | 1 + .../_delivery/domain/menu/MenuRepository.java | 3 + .../_delivery/domain/menu/MenuService.java | 21 --- .../domain/menu/MenuIntegrationTest.java | 132 +++++++++++++++++- 4 files changed, 131 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/sparta/spring_deep/_delivery/admin/ai/AiRepository.java b/src/main/java/com/sparta/spring_deep/_delivery/admin/ai/AiRepository.java index e7dc10e..2579802 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/admin/ai/AiRepository.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/admin/ai/AiRepository.java @@ -8,4 +8,5 @@ @Repository public interface AiRepository extends JpaRepository, AiRepositoryCustom { + boolean existsByMenuId(UUID menuId); } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRepository.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRepository.java index c758b5e..ea39d62 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRepository.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRepository.java @@ -1,5 +1,6 @@ package com.sparta.spring_deep._delivery.domain.menu; +import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; import java.util.Optional; import java.util.UUID; import org.springframework.data.jpa.repository.JpaRepository; @@ -9,4 +10,6 @@ public interface MenuRepository extends JpaRepository, MenuRepositoryCustom { Optional findByIdAndIsDeletedFalse(UUID menuId); + + boolean existsByRestaurantAndName(Restaurant restaurant, String name); } diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuService.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuService.java index cc23520..56a8557 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuService.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuService.java @@ -9,7 +9,6 @@ import com.sparta.spring_deep._delivery.domain.restaurant.RestaurantRepository; import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; import com.sparta.spring_deep._delivery.domain.user.entity.User; -import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; import com.sparta.spring_deep._delivery.exception.ResourceNotFoundException; import com.sparta.spring_deep._delivery.util.AuthTools; import java.util.UUID; @@ -26,7 +25,6 @@ public class MenuService { private final MenuRepository menuRepository; - private final UserRepository userRepository; private final GoogleAiService googleAiService; private final AiRepository aiRepository; private final RestaurantRepository restaurantRepository; @@ -63,25 +61,6 @@ public MenuResponseDto addMenu( return new MenuResponseDto(menu); } -// // restaurant_id 기반 모든 메뉴 조회 -// public Page getAllMenus( -// Restaurant restaurantId, -// String name, -// String sortBy, -// int page, int size -// ) { -// log.info("restaurant_id 기반 모든 메뉴 조회"); -// -// int pageSize = (size == 10 || size == 30 || size == 50) ? size : 10; -// -// Pageable pageable = PageRequest.of(page, pageSize, Sort.by(Direction.DESC, sortBy)); -// -// Page menus = menuRepository.findAllByRestaurantIdAndIsDeletedFalse(restaurantId, -// pageable); -// -// return menus.map(MenuResponseDto::new); -// } - public Page searchMenus(UUID restaurantId, MenuSearchDto searchDto, Pageable pageable) { log.info("restaurant_id 기반 모든 메뉴 검색 및 조회"); diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java index 63d1592..e493bc2 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java @@ -2,11 +2,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; +import com.sparta.spring_deep._delivery.admin.ai.AiRepository; import com.sparta.spring_deep._delivery.domain.restaurant.Restaurant; import com.sparta.spring_deep._delivery.domain.restaurant.RestaurantRepository; import com.sparta.spring_deep._delivery.domain.restaurant.restaurantAddress.RestaurantAddress; @@ -18,7 +20,6 @@ import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; import com.sparta.spring_deep._delivery.testutil.TestEntityCreateTools; import java.math.BigDecimal; -import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -53,13 +54,19 @@ public class MenuIntegrationTest { @Autowired private ObjectMapper objectMapper; + @Autowired + private MenuRepository menuRepository; + + @Autowired + private AiRepository aiRepository; + private User owner; private User customer; private User otherOwner; private Restaurant testRestaurant; private RestaurantAddress testRestaurantAddress; - @Autowired - private MenuRepository menuRepository; + private Menu testMenu; + @BeforeEach void setUp() { @@ -105,6 +112,16 @@ void setUp() { "010-1111-1111"); restaurantRepository.save(testRestaurant); + // AI 설명 생성용 testMenu 생성 + testMenu = TestEntityCreateTools.createMenu( + owner, + testRestaurant, + "감자볶음", + "감자를 식용유에 두르고 볶았습니다.", + 20000.00 + ); + menuRepository.save(testMenu); + } private Authentication getAuth(User user) { @@ -113,6 +130,7 @@ private Authentication getAuth(User user) { userDetails.getAuthorities()); } + // 메뉴 추가 성공 테스트. @Test void addMenuTest() throws Exception { MenuRequestDto requestDto = new MenuRequestDto(); @@ -133,8 +151,7 @@ void addMenuTest() throws Exception { String responseBody = mvcResult.getResponse().getContentAsString(); MenuResponseDto responseDto = objectMapper.readValue(responseBody, MenuResponseDto.class); - UUID addedMenuId = responseDto.getId(); - Menu savedMenu = menuRepository.findById(addedMenuId) + Menu savedMenu = menuRepository.findById(responseDto.getId()) .orElseThrow(() -> new RuntimeException("DB에 저장된 메뉴를 찾을 수 없습니다.")); assertEquals("왕감자", savedMenu.getName()); @@ -144,4 +161,109 @@ void addMenuTest() throws Exception { assertEquals(testRestaurant.getId(), savedMenu.getRestaurant().getId()); } + // 메뉴 추가 실패 테스트 : 다른 레스토랑 오너 + @Test + void addMenuOtherOwner() throws Exception { + MenuRequestDto requestDto = new MenuRequestDto(); + requestDto.setName("다른 오너에요"); + requestDto.setDescription("다른오너가 추가한 메뉴"); + requestDto.setPrice(BigDecimal.valueOf(15000)); + requestDto.setIsHidden(false); + + MvcResult mvcResult = mockMvc.perform( + post("/api/menus/{restaurantId}", testRestaurant.getId()) + .with(authentication(getAuth(otherOwner))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isForbidden()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + System.out.println("responseBody = " + responseBody); + + boolean exists = menuRepository.existsByRestaurantAndName(testRestaurant, + requestDto.getName()); + assertFalse(exists, "실패 : 다른 오너가 추가한 메뉴가 DB에 생성됨"); + + } + + // 메뉴 추가 실패 테스트 : 고객 + @Test + void addMenuCustomer() throws Exception { + MenuRequestDto requestDto = new MenuRequestDto(); + requestDto.setName("손님이에요"); + requestDto.setDescription("손님이 추가한 메뉴"); + requestDto.setPrice(BigDecimal.valueOf(15000)); + requestDto.setIsHidden(false); + + MvcResult mvcResult = mockMvc.perform( + post("/api/menus/{restaurantId}", testRestaurant.getId()) + .with(authentication(getAuth(customer))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isForbidden()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + System.out.println("responseBody = " + responseBody); + + boolean exists = menuRepository.existsByRestaurantAndName(testRestaurant, + requestDto.getName()); + assertFalse(exists, "실패 : 손님이 추가한 메뉴가 DB에 생성됨"); + + } + + // AI 로그 생성 성공 테스트 + @Test + void createAiDescription() throws Exception { + + mockMvc.perform( + post("/api/menus/{menuId}/aiDescription", testMenu.getId()) + .with(authentication(getAuth(owner))) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isCreated()) + .andReturn(); + + boolean exists = aiRepository.existsByMenuId(testMenu.getId()); + assertTrue(exists, "실패 : Ai 로그가 생성되지 않았습니다."); + + } + + // AI 로그 생성 실패 테스트 : 다른 레스토랑 오너 + @Test + void createAiDescriptionOtherOwner() throws Exception { + + mockMvc.perform( + post("/api/menus/{menuId}/aiDescription", testMenu.getId()) + .with(authentication(getAuth(otherOwner))) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()) + .andReturn(); + + boolean exists = aiRepository.existsByMenuId(testMenu.getId()); + assertFalse(exists, "실패 : 다른 레스토랑 주인으로부터 Ai 로그가 생성되었습니다."); + + } + + // AI 로그 생성 실패 테스트 : 고객 + @Test + void createAiDescriptionCustomer() throws Exception { + + mockMvc.perform( + post("/api/menus/{menuId}/aiDescription", testMenu.getId()) + .with(authentication(getAuth(customer))) + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isForbidden()) + .andReturn(); + + boolean exists = aiRepository.existsByMenuId(testMenu.getId()); + assertFalse(exists, "실패 : 고객으로부터 Ai 로그가 생성되었습니다."); + + } + } From 85d8374c3eb6ab516afc9950fdf5c9318a4ebc4a Mon Sep 17 00:00:00 2001 From: Imnotcoderdude Date: Tue, 25 Feb 2025 04:44:42 +0900 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=8D=BB=20test=20:=20Menu,=20address?= =?UTF-8?q?=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20=EB=B0=B0=EC=86=A1=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=84=B1=EA=B3=B5,=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20-=20=EB=A9=94=EB=89=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=84=B1=EA=B3=B5,=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20-=20=EB=A9=94=EB=89=B4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=84=B1=EA=B3=B5,=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../address/controller/AddressController.java | 3 +- .../domain/address/dto/AddressRequestDto.java | 8 +- .../address/dto/AddressResponseDto.java | 10 +- .../address/repository/AddressRepository.java | 4 + .../_delivery/domain/menu/MenuRequestDto.java | 4 + ...est.java => UserAdminIntegrationTest.java} | 2 +- .../address/AddressIntegrationTest.java | 140 +++++++++++++ .../domain/menu/MenuIntegrationTest.java | 189 +++++++++++++++++- 8 files changed, 345 insertions(+), 15 deletions(-) rename src/test/java/com/sparta/spring_deep/_delivery/admin/user/{UserAdminApiTest.java => UserAdminIntegrationTest.java} (98%) create mode 100644 src/test/java/com/sparta/spring_deep/_delivery/domain/address/AddressIntegrationTest.java diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/controller/AddressController.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/controller/AddressController.java index aa82fa2..088c7f3 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/controller/AddressController.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/controller/AddressController.java @@ -12,6 +12,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -41,7 +42,7 @@ public ResponseEntity createAddress( AddressResponseDto responseDto = addressService.createAddress(requestDto, userDetails); - return ResponseEntity.ok(responseDto); + return ResponseEntity.status(HttpStatus.CREATED).body(responseDto); } // 배송지 조회 diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressRequestDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressRequestDto.java index b4f99a2..40b0a13 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressRequestDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressRequestDto.java @@ -1,8 +1,12 @@ package com.sparta.spring_deep._delivery.domain.address.dto; -import lombok.Getter; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; -@Getter +@Data +@NoArgsConstructor +@AllArgsConstructor public class AddressRequestDto { private String addressId; diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressResponseDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressResponseDto.java index adbeeee..6e2fc34 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressResponseDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/dto/AddressResponseDto.java @@ -2,11 +2,13 @@ import com.sparta.spring_deep._delivery.domain.address.entity.Address; import java.util.UUID; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; -@Getter -@RequiredArgsConstructor +@Data +@AllArgsConstructor +@NoArgsConstructor public class AddressResponseDto { private UUID id; diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/repository/AddressRepository.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/repository/AddressRepository.java index 8c6cf1d..64f478b 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/address/repository/AddressRepository.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/address/repository/AddressRepository.java @@ -1,6 +1,7 @@ package com.sparta.spring_deep._delivery.domain.address.repository; import com.sparta.spring_deep._delivery.domain.address.entity.Address; +import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -20,4 +21,7 @@ public interface AddressRepository extends JpaRepository, Address Optional
findByIdAndIsDeletedFalse(UUID addressId); List
findAllByUserUsernameAndIsDeletedFalse(String username); + + + boolean existsByAddressName(@NotNull String addressName); } \ No newline at end of file diff --git a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRequestDto.java b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRequestDto.java index 3c858c6..3b3b8be 100644 --- a/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRequestDto.java +++ b/src/main/java/com/sparta/spring_deep/_delivery/domain/menu/MenuRequestDto.java @@ -1,9 +1,13 @@ package com.sparta.spring_deep._delivery.domain.menu; import java.math.BigDecimal; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@AllArgsConstructor +@NoArgsConstructor public class MenuRequestDto { private String name; diff --git a/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java b/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminIntegrationTest.java similarity index 98% rename from src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java rename to src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminIntegrationTest.java index 4915966..7e09359 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminApiTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/admin/user/UserAdminIntegrationTest.java @@ -32,7 +32,7 @@ @SpringBootTest @AutoConfigureMockMvc @Transactional -public class UserAdminApiTest { +public class UserAdminIntegrationTest { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/address/AddressIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/address/AddressIntegrationTest.java new file mode 100644 index 0000000..d8c2ace --- /dev/null +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/address/AddressIntegrationTest.java @@ -0,0 +1,140 @@ +package com.sparta.spring_deep._delivery.domain.address; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sparta.spring_deep._delivery.domain.address.dto.AddressRequestDto; +import com.sparta.spring_deep._delivery.domain.address.dto.AddressResponseDto; +import com.sparta.spring_deep._delivery.domain.address.entity.Address; +import com.sparta.spring_deep._delivery.domain.address.repository.AddressRepository; +import com.sparta.spring_deep._delivery.domain.user.details.UserDetailsImpl; +import com.sparta.spring_deep._delivery.domain.user.entity.IsPublic; +import com.sparta.spring_deep._delivery.domain.user.entity.User; +import com.sparta.spring_deep._delivery.domain.user.entity.UserRole; +import com.sparta.spring_deep._delivery.domain.user.repository.UserRepository; +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.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@AutoConfigureMockMvc +@Transactional +public class AddressIntegrationTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserRepository userRepository; + + @Autowired + private AddressRepository addressRepository; + + private User customer; + private User owner; + private Address address; + + @BeforeEach + void setUp() { + customer = User.builder() + .username("userTest") + .password("userTest1234*") + .email("testuser@test.com") + .role(UserRole.CUSTOMER) + .isPublic(IsPublic.PUBLIC) + .build(); + customer = userRepository.save(customer); + + owner = User.builder() + .username("owner") + .password("owner") + .email("owner@test.com") + .role(UserRole.OWNER) + .isPublic(IsPublic.PUBLIC) + .build(); + owner = userRepository.save(owner); + + } + + // 인증 객체 생성 + private Authentication getAuth(User user) { + UserDetailsImpl userDetails = new UserDetailsImpl(user); + return new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + } + + @Test + @DisplayName("배송지 추가 테스트 : 성공") + void createAddressTest() throws Exception { + String testAddress = "서울시 강남구 테헤란로 123"; + String testAddressName = "회사"; + + AddressRequestDto requestDto = new AddressRequestDto(); + requestDto.setAddress(testAddress); + requestDto.setAddressName(testAddressName); + + MvcResult mvcResult = mockMvc.perform( + post("/api/addresses") + .with(authentication(getAuth(customer))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isCreated()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + AddressResponseDto responseDto = objectMapper.readValue(responseBody, + AddressResponseDto.class); + + Address createdAddress = addressRepository.findById(responseDto.getId()) + .orElseThrow(() -> new RuntimeException("해당하는 배송지가 없습니다")); + + assertEquals(testAddressName, createdAddress.getAddressName()); + assertEquals(testAddress, createdAddress.getAddress()); + + } + + @Test + @DisplayName("배송지 추가 테스트 : 실패") + void createAddressOwnerTest() throws Exception { + String testAddress = "안산시 상록구 부곡동 123"; + String testAddressName = "추가되면안되는집"; + + AddressRequestDto requestDto = new AddressRequestDto(); + requestDto.setAddress(testAddress); + requestDto.setAddressName(testAddressName); + + MvcResult mvcResult = mockMvc.perform( + post("/api/addresses") + .with(authentication(getAuth(owner))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isForbidden()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + System.out.println("responseBody = " + responseBody); + + boolean exists = addressRepository.existsByAddressName(requestDto.getAddressName()); + assertFalse(exists, "실패 : 오너가 추가한 배송지가 DB에 생성됨"); + + } + +} diff --git a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java index e493bc2..59d0bb9 100644 --- a/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java +++ b/src/test/java/com/sparta/spring_deep/_delivery/domain/menu/MenuIntegrationTest.java @@ -4,7 +4,10 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +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.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; @@ -21,6 +24,7 @@ import com.sparta.spring_deep._delivery.testutil.TestEntityCreateTools; import java.math.BigDecimal; 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.web.servlet.AutoConfigureMockMvc; @@ -66,6 +70,9 @@ public class MenuIntegrationTest { private Restaurant testRestaurant; private RestaurantAddress testRestaurantAddress; private Menu testMenu; + private Menu testMenu1; + private Menu testMenu2; + private Menu testMenu3; @BeforeEach @@ -117,11 +124,38 @@ void setUp() { owner, testRestaurant, "감자볶음", - "감자를 식용유에 두르고 볶았습니다.", + "감자에 식용유를 두르고 볶았습니다.", 20000.00 ); menuRepository.save(testMenu); + testMenu1 = TestEntityCreateTools.createMenu( + owner, + testRestaurant, + "당근볶음", + "당근에 식용유를 두르고 볶았습니다.", + 20000.00 + ); + menuRepository.save(testMenu1); + + testMenu2 = TestEntityCreateTools.createMenu( + owner, + testRestaurant, + "가지볶음", + "가지에 식용유를 두르고 볶았습니다.", + 20000.00 + ); + menuRepository.save(testMenu2); + + testMenu3 = TestEntityCreateTools.createMenu( + owner, + testRestaurant, + "제육볶음", + "제육에 식용유를 두르고 볶았습니다.", + 20000.00 + ); + menuRepository.save(testMenu3); + } private Authentication getAuth(User user) { @@ -130,8 +164,9 @@ private Authentication getAuth(User user) { userDetails.getAuthorities()); } - // 메뉴 추가 성공 테스트. + // 메뉴 추가 테스트 : 성공 @Test + @DisplayName("메뉴 추가 테스트 : 성공") void addMenuTest() throws Exception { MenuRequestDto requestDto = new MenuRequestDto(); requestDto.setName("왕감자"); @@ -161,8 +196,9 @@ void addMenuTest() throws Exception { assertEquals(testRestaurant.getId(), savedMenu.getRestaurant().getId()); } - // 메뉴 추가 실패 테스트 : 다른 레스토랑 오너 + // 메뉴 추가 다른 레스토랑 오너 테스트 : 실패 @Test + @DisplayName("메뉴 추가 다른 레스토랑 오너 테스트 : 실패") void addMenuOtherOwner() throws Exception { MenuRequestDto requestDto = new MenuRequestDto(); requestDto.setName("다른 오너에요"); @@ -188,8 +224,9 @@ void addMenuOtherOwner() throws Exception { } - // 메뉴 추가 실패 테스트 : 고객 + // 메뉴 추가 고객 테스트 : 실패 @Test + @DisplayName("메뉴 추가 고객 테스트 : 실패") void addMenuCustomer() throws Exception { MenuRequestDto requestDto = new MenuRequestDto(); requestDto.setName("손님이에요"); @@ -215,8 +252,144 @@ void addMenuCustomer() throws Exception { } - // AI 로그 생성 성공 테스트 + // 메뉴 단건 조회 테스트 : 성공 + @Test + @DisplayName("메뉴 단건 조회 테스트 : 성공") + void searchMenu() throws Exception { + MvcResult mvcResult = mockMvc.perform( + get("/api/menus/{restaurantId}/search", testRestaurant.getId()) + .with(authentication(getAuth(owner))) + .param("name", "감자볶음") // 예: MenuSearchDto의 필드 + .param("page", "0") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + assertTrue(responseBody.contains("감자볶음"), "검색 결과에 '감자볶음'이 없음"); + } + + // 메뉴 단건 조회 테스트 : 실패 + @Test + @DisplayName("메뉴 단건 조회 테스트 : 실패") + void searchMenus_NoResult() throws Exception { + mockMvc.perform( + get("/api/menus/{restaurantId}/search", testRestaurant.getId()) + .with(authentication(getAuth(customer))) + .param("name", "탕수육") + .param("page", "0") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isNotFound()); + } + + // 메뉴 다건 조회 테스트 : 성공 + @Test + @DisplayName("메뉴 다건 조회 테스트 : 성공") + void searchAllMenus() throws Exception { + MvcResult mvcResult = mockMvc.perform( + get("/api/menus/{restaurantId}/search", testRestaurant.getId()) + .with(authentication(getAuth(customer))) + .param("page", "0") + .param("size", "10") + .contentType(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + System.out.println("responseBody = " + responseBody); + + assertTrue(responseBody.contains(testMenu.getName()), "전체 조회 결과에 기존 메뉴가 없음"); + } + + // 메뉴 수정 테스트 : 성공 + @Test + @DisplayName("메뉴 수정 테스트 : 성공") + void updateMenu_Success() throws Exception { + MenuRequestDto requestDto = new MenuRequestDto("감자튀김", "튀긴 감자", BigDecimal.valueOf(9999), + true); + + MvcResult mvcResult = mockMvc.perform( + put("/api/menus/{menuId}", testMenu.getId()) + .with(authentication(getAuth(owner))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = mvcResult.getResponse().getContentAsString(); + MenuResponseDto responseDto = objectMapper.readValue(responseBody, MenuResponseDto.class); + + Menu updatedMenu = menuRepository.findById(responseDto.getId()) + .orElseThrow(() -> new RuntimeException("업데이트된 메뉴가 DB에 없음")); + + assertEquals("감자튀김", updatedMenu.getName()); + assertEquals("튀긴 감자", updatedMenu.getDescription()); + assertEquals(BigDecimal.valueOf(9999), updatedMenu.getPrice()); + assertTrue(updatedMenu.getIsHidden()); + } + + // 메뉴 수정 다른 사장님 테스트 : 실패 + @Test + @DisplayName("메뉴 수정 다른 사장님 테스트 : 실패") + void updateMenu_OtherOwner() throws Exception { + MenuRequestDto requestDto = new MenuRequestDto("다른 오너 메뉴", "다른오너 수정", + BigDecimal.valueOf(20000), false); + + mockMvc.perform( + put("/api/menus/{menuId}", testMenu.getId()) + .with(authentication(getAuth(otherOwner))) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + ) + .andExpect(status().isForbidden()); + + // DB에 변경이 없는지 확인 + Menu unchanged = menuRepository.findById(testMenu.getId()) + .orElseThrow(() -> new RuntimeException("메뉴가 없어졌음?")); + assertEquals(testMenu.getName(), unchanged.getName()); + assertEquals(testMenu.getDescription(), unchanged.getDescription()); + } + + // 메뉴 삭제 테스트 : 성공 + @Test + @DisplayName("메뉴 삭제 테스트 : 성공") + void deleteMenu_Success() throws Exception { + mockMvc.perform( + delete("/api/menus/{menuId}", testMenu.getId()) + .with(authentication(getAuth(owner))) + ) + .andExpect(status().isOk()); + + // 소프트 삭제인지 여부 확인 + boolean exists = menuRepository.findByIdAndIsDeletedFalse(testMenu.getId()).isPresent(); + assertFalse(exists, "삭제했는데도 isDeleted=false 상태로 남아있음"); + } + + // 메뉴 삭제 다른 사장님 테스트 : 실패 + @Test + @DisplayName("메뉴 삭제 다른 사장님 테스트 : 실패") + void deleteMenu_OtherOwner() throws Exception { + mockMvc.perform( + delete("/api/menus/{menuId}", testMenu.getId()) + .with(authentication(getAuth(otherOwner))) + ) + .andExpect(status().isForbidden()); + + // DB에 여전히 isDeleted=false인지 + boolean stillExists = menuRepository.findByIdAndIsDeletedFalse(testMenu.getId()) + .isPresent(); + assertTrue(stillExists, "다른 사장님이 삭제했는데 메뉴가 삭제됨"); + } + + // AI 로그 생성 테스트 : 성공 @Test + @DisplayName("AI 로그 생성 테스트 : 성공") void createAiDescription() throws Exception { mockMvc.perform( @@ -232,8 +405,9 @@ void createAiDescription() throws Exception { } - // AI 로그 생성 실패 테스트 : 다른 레스토랑 오너 + // AI 로그 생성 다른 사장님 테스트 : 실패 @Test + @DisplayName("AI 로그 생성 다른 사장님 테스트 : 실패") void createAiDescriptionOtherOwner() throws Exception { mockMvc.perform( @@ -249,8 +423,9 @@ void createAiDescriptionOtherOwner() throws Exception { } - // AI 로그 생성 실패 테스트 : 고객 + // AI 로그 생성 고객 테스트 : 실패 @Test + @DisplayName("AI 로그 생성 고객 테스트 : 실패") void createAiDescriptionCustomer() throws Exception { mockMvc.perform(